diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/controller/onClickActions/CardOnClickActions.kt b/app/src/main/java/com/belmontCrest/cardCrafter/controller/onClickActions/CardOnClickActions.kt index 809a1a1..d88599e 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/controller/onClickActions/CardOnClickActions.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/controller/onClickActions/CardOnClickActions.kt @@ -1,23 +1,41 @@ package com.belmontCrest.cardCrafter.controller.onClickActions +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +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.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import com.belmontCrest.cardCrafter.controller.viewModels.cardViewsModels.EditCardViewModel import com.belmontCrest.cardCrafter.localDatabase.tables.CT import com.belmontCrest.cardCrafter.localDatabase.tables.Card import com.belmontCrest.cardCrafter.model.ui.Fields import com.belmontCrest.cardCrafter.ui.theme.GetUIStyle -import com.belmontCrest.cardCrafter.ui.theme.deleteTextColor import kotlinx.coroutines.CoroutineScope import com.belmontCrest.cardCrafter.R import com.belmontCrest.cardCrafter.controller.cardHandlers.toCard +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck +import com.belmontCrest.cardCrafter.model.TAProp +import com.belmontCrest.cardCrafter.model.TCProp +import com.belmontCrest.cardCrafter.model.TextProps import com.belmontCrest.cardCrafter.model.Type import com.belmontCrest.cardCrafter.navigation.NavViewModel +import com.belmontCrest.cardCrafter.ui.theme.mainViewModifier +import com.belmontCrest.cardCrafter.uiFunctions.CustomText import com.belmontCrest.cardCrafter.uiFunctions.buttons.CancelButton import com.belmontCrest.cardCrafter.uiFunctions.buttons.SubmitButton import kotlinx.coroutines.launch @@ -228,7 +246,7 @@ fun DeleteCard( ) }, confirmButton = { - Button( + SubmitButton( onClick = { pressed.value = false fields.mainClicked.value = false @@ -236,14 +254,9 @@ fun DeleteCard( navViewModel.deleteCard(card) onDelete() } - }, - colors = ButtonDefaults.buttonColors( - containerColor = getUIStyle.secondaryButtonColor(), - contentColor = deleteTextColor - ) - ) { - Text("OK") - } + }, enabled = true, getUIStyle = getUIStyle, + string = stringResource(R.string.okay) + ) }, dismissButton = { CancelButton(onClick = { pressed.value = false }, enabled = true, getUIStyle) @@ -278,5 +291,85 @@ fun DeleteCards( } ) } +} +@Composable +fun DuplicateCards( + showDialog: Boolean, onDismiss: (Boolean) -> Unit, + onDuplicate: () -> Unit, getUIStyle: GetUIStyle, enabled: Boolean +) { + if (showDialog) { + AlertDialog( + onDismissRequest = { if (enabled) onDismiss(false) }, + title = { Text(stringResource(R.string.duplicate_card_list)) }, + text = { + Text( + text = stringResource(R.string.sure_to_duplicate_card_list), + color = getUIStyle.titleColor() + ) + }, + confirmButton = { + SubmitButton( + onClick = { onDuplicate() }, enabled = enabled, + getUIStyle, stringResource(R.string.okay) + ) + }, + dismissButton = { + CancelButton(onClick = { onDismiss(false) }, enabled, getUIStyle) + } + ) + } +} + +@Composable +fun CopyMoveCardList( + showDialog: Boolean, onDismiss: (Boolean) -> Unit, getUIStyle: GetUIStyle, + deckList: List, onCopyOrMove: (Int) -> Unit, enabled: Boolean, selectedDeck: Deck? +) { + if (showDialog) { + Dialog( + onDismissRequest = { onDismiss(false) } + ) { + Column( + modifier = Modifier + .fillMaxSize(0.9f) + .background( + color = getUIStyle.altBackground(), shape = RoundedCornerShape(16.dp) + ), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top + ) { + LazyColumn( + modifier = Modifier + .fillMaxWidth(), + contentPadding = PaddingValues( + vertical = 24.dp, + horizontal = 6.dp + ) + ) { + items(deckList) { deck -> + CustomText( + text = deck.name, + getUIStyle = getUIStyle, + modifier = Modifier + .padding(4.dp) + .mainViewModifier(getUIStyle.getColorScheme()) + .clickable(enabled = deck.id != selectedDeck?.id) { + onCopyOrMove(deck.id) + }, + props = TextProps( + ta = TAProp.Center, + tc = + if (deck.id != selectedDeck?.id) TCProp.Default + else TCProp.Disabled + ) + ) + } + } + CancelButton( + onClick = { onDismiss(false) }, enabled = enabled, getUIStyle = getUIStyle + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/ReusedFunc.kt b/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/ReusedFunc.kt new file mode 100644 index 0000000..66ba7cd --- /dev/null +++ b/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/ReusedFunc.kt @@ -0,0 +1,53 @@ +package com.belmontCrest.cardCrafter.controller.viewModels + +import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.FlashCardRepository +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import java.util.Date + +class ReusedFunc(private val flashCardRepository: FlashCardRepository) { + companion object { + private const val TIMEOUT_MILLIS = 4_000L + } + suspend fun updateCardsLeft(deck: Deck, cardsToAdd: Int) { + return withContext(Dispatchers.IO) { + /** Only add the cards if the deck's review is due */ + if (deck.nextReview <= Date()) { + withTimeout(TIMEOUT_MILLIS) { + /** This keeps a record of the amount of cards left vs done and compares + * it to the card amount. Here's three examples + * cardsLeft = CL, cardsDone = CD, cardAmount = CA + * 1 represents how many cardsToAdd + * CA = 20 + * 1. CL = 19, CD = 1 + * (CL + 1 = 20) < CA && (CD + 1 = 2) < CA -> False, + * (CD + 1 = 2) >= CA -> False; just update to CA + * + * 2. CL = 1, CD = 19 + * (CL + 1 = 2) && (CD + 1 = 20) < CA -> F; move down + * (CD + 1 = 20) >= CA -> True, Don't update just return. + * + * 3. CL = 15, CD = 5 + * (CL + 1 = 16) ** (CD + 1 = 6) < CA -> True; Update accordingly. + */ + if (((deck.cardsLeft + cardsToAdd) < deck.cardAmount) && + ((deck.cardsDone + cardsToAdd) < deck.cardAmount) + ) { + flashCardRepository.updateCardsLeft( + deckId = deck.id, cardsDone = deck.cardsDone, + cardsLeft = (deck.cardsLeft + cardsToAdd), + ) + } else if ((deck.cardsDone + cardsToAdd) >= deck.cardAmount) { + return@withTimeout + } else { + flashCardRepository.updateCardsLeft( + deck.id, deck.cardAmount, deck.cardsDone + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/AddCardViewModel.kt b/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/AddCardViewModel.kt index 304a37d..d11e32d 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/AddCardViewModel.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/AddCardViewModel.kt @@ -3,6 +3,7 @@ package com.belmontCrest.cardCrafter.controller.viewModels.cardViewsModels import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.belmontCrest.cardCrafter.controller.viewModels.ReusedFunc import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.FlashCardRepository import com.belmontCrest.cardCrafter.localDatabase.tables.Deck import com.belmontCrest.cardCrafter.localDatabase.tables.PartOfQorA @@ -13,9 +14,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlinx.coroutines.withTimeout -import java.util.Date class AddCardViewModel( private val flashCardRepository: FlashCardRepository, @@ -25,10 +23,11 @@ class AddCardViewModel( private val _errorMessage: MutableStateFlow = MutableStateFlow("") val errorMessage = _errorMessage.asStateFlow() private val isOwner = MutableStateFlow(false) + private val rf = ReusedFunc(flashCardRepository) - companion object { + /*companion object { private const val TIMEOUT_MILLIS = 4_000L - } + }*/ init { viewModelScope.launch { @@ -64,7 +63,7 @@ class AddCardViewModel( fun addThreeCard( deck: Deck, question: String, - middle: String, answer: String, isQOrA : PartOfQorA + middle: String, answer: String, isQOrA: PartOfQorA ) { if (question.isNotBlank() && answer.isNotBlank() && middle.isNotBlank() @@ -119,44 +118,7 @@ class AddCardViewModel( suspend fun updateCardsLeft(deck: Deck, cardsToAdd: Int = 1) { - return withContext(Dispatchers.IO) { - /** Only add the cards if the deck's review is due */ - if (deck.nextReview <= Date()) { - viewModelScope.launch(Dispatchers.IO) { - withTimeout(TIMEOUT_MILLIS) { - /** This keeps a record of the amount of cards left vs done and compares - * it to the card amount. Here's three examples - * cardsLeft = CL, cardsDone = CD, cardAmount = CA - * CA = 20 - * 1. CL = 19, CD = 1 - * CL + 1 = 20, CD + 1 = 2, ! < CA, F; move down - * CD + 1 = 2, ! >= CA, F; just update to CA - * - * 2. CL = 1, CD = 19 - * CL + 1 = 2, CD + 1 = 20, ! < CA, F; move down - * CD + 1 = 20 >= CA, T, Don't update just return. - * - * 3. CL = 15, CD = 5 - * CL + 1 = 16, CD + 1 = 6, 16 && 6 < CA, T; Update accordingly. - */ - if (((deck.cardsLeft + cardsToAdd) < deck.cardAmount) && - ((deck.cardsDone + cardsToAdd) < deck.cardAmount) - ) { - flashCardRepository.updateCardsLeft( - deckId = deck.id, cardsDone = deck.cardsDone, - cardsLeft = (deck.cardsLeft + cardsToAdd), - ) - } else if ((deck.cardsDone + cardsToAdd) >= deck.cardAmount) { - return@withTimeout - } else { - flashCardRepository.updateCardsLeft( - deck.id, deck.cardAmount, deck.cardsDone - ) - } - } - } - } - } + rf.updateCardsLeft(deck, cardsToAdd) } fun setErrorMessage(message: String) { diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/CardDeckViewModel.kt b/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/CardDeckViewModel.kt index c37d7a8..a4735ca 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/CardDeckViewModel.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/controller/viewModels/cardViewsModels/CardDeckViewModel.kt @@ -69,7 +69,7 @@ class CardDeckViewModel( if (dueDetails == null) { flowOf(SealedDueCTs()) } else { - cardTypeRepository.getAllDueCards(dueDetails.id, dueDetails.cardsLeft, Date().time) + cardTypeRepository.getAllDueCardsStream(dueDetails.id, dueDetails.cardsLeft, Date().time) .map { transitionTo(CardState.Finished) SealedDueCTs( diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/CardTypesDao.kt b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/CardTypesDao.kt index 57213ed..32647cc 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/CardTypesDao.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/CardTypesDao.kt @@ -2,8 +2,6 @@ package com.belmontCrest.cardCrafter.localDatabase.dbInterface.daoInterfaces.dec import androidx.room.Dao import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import com.belmontCrest.cardCrafter.controller.cardHandlers.toBasicList @@ -16,10 +14,12 @@ import com.belmontCrest.cardCrafter.localDatabase.tables.AllCardTypes import com.belmontCrest.cardCrafter.localDatabase.tables.BasicCard import com.belmontCrest.cardCrafter.localDatabase.tables.CT import com.belmontCrest.cardCrafter.localDatabase.tables.Card +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck import com.belmontCrest.cardCrafter.localDatabase.tables.HintCard import com.belmontCrest.cardCrafter.localDatabase.tables.MultiChoiceCard import com.belmontCrest.cardCrafter.localDatabase.tables.NotationCard import com.belmontCrest.cardCrafter.localDatabase.tables.ThreeFieldCard +import com.belmontCrest.cardCrafter.model.InsertOrAbortDao import com.belmontCrest.cardCrafter.model.Type.BASIC import com.belmontCrest.cardCrafter.model.Type.HINT import com.belmontCrest.cardCrafter.model.Type.MULTI @@ -27,9 +27,10 @@ import com.belmontCrest.cardCrafter.model.Type.NOTATION import com.belmontCrest.cardCrafter.model.Type.THREE import com.belmontCrest.cardCrafter.model.ui.Fields import kotlinx.coroutines.flow.Flow +import java.util.Date @Dao -interface CardTypesDao { +interface CardTypesDao : InsertOrAbortDao { @Transaction @Query( """SELECT * FROM cards WHERE deckId = :deckId @@ -63,7 +64,6 @@ interface CardTypesDao { ) suspend fun getAllCardTypes(deckId: Int): List - @Transaction @Query("""SELECT * FROM cards where id = :id""") fun getACardType(id: Int): AllCardTypes @@ -72,20 +72,6 @@ interface CardTypesDao { @Query("""SELECT * FROM cards where id = :id""") fun getACardTypeStream(id: Int): Flow - @Insert(onConflict = OnConflictStrategy.Companion.ABORT) - suspend fun insertBasicCard(basicCard: BasicCard) - - @Insert(onConflict = OnConflictStrategy.Companion.ABORT) - suspend fun insertThreeCard(threeFieldCard: ThreeFieldCard) - - @Insert(onConflict = OnConflictStrategy.Companion.ABORT) - suspend fun insertHintCard(hintCard: HintCard) - - @Insert(onConflict = OnConflictStrategy.Companion.ABORT) - suspend fun insertMultiChoiceCard(multiChoiceCard: MultiChoiceCard) - - @Insert(onConflict = OnConflictStrategy.Companion.ABORT) - suspend fun insertNotationCard(notationCard: NotationCard) @Query("Update cards set type = :type where id = :cardId") suspend fun updateCard(cardId: Int, type: String) @@ -172,25 +158,11 @@ interface CardTypesDao { } updateCard(cardId, type) when (deleteCT) { - is CT.Basic -> { - deleteBasicCard(deleteCT.basicCard) - } - - is CT.ThreeField -> { - deleteThreeCard(deleteCT.threeFieldCard) - } - - is CT.Hint -> { - deleteHintCard(deleteCT.hintCard) - } - - is CT.MultiChoice -> { - deleteMultiChoiceCard(deleteCT.multiChoiceCard) - } - - is CT.Notation -> { - deleteNotationCard(deleteCT.notationCard) - } + is CT.Basic -> deleteBasicCard(deleteCT.basicCard) + is CT.ThreeField -> deleteThreeCard(deleteCT.threeFieldCard) + is CT.Hint -> deleteHintCard(deleteCT.hintCard) + is CT.MultiChoice -> deleteMultiChoiceCard(deleteCT.multiChoiceCard) + is CT.Notation -> deleteNotationCard(deleteCT.notationCard) } } @@ -212,6 +184,7 @@ interface CardTypesDao { @Delete suspend fun deleteNotationCards(notationCards: List) + /** Delete the selected cards */ @Transaction suspend fun deleteCardList(cts: List) { val basicCards = cts.toBasicList() @@ -224,4 +197,83 @@ interface CardTypesDao { deleteHintCards(hintCards); deleteMultiCards(multiCards) deleteNotationCards(notationCards) } + + + @Query("SELECT MAX(deckCardNumber) FROM cards WHERE deckId = :deckId") + fun getMaxDCNumber(deckId: Int): Int? + + /** Copy the selected cards into a new deck */ + @Transaction + suspend fun copyCardList(cts: List, deck: Deck) { + cts.map { ct -> + val newDeckCardNumber = returnCardDeckNum(deck.id) + when (ct) { + is CT.Basic -> { + val cardId = returnCard(deck, newDeckCardNumber, BASIC).toInt() + val bc = ct.basicCard + insertBasicCard(BasicCard(cardId, bc.question, bc.answer)) + } + + is CT.Hint -> { + val cardId = returnCard(deck, newDeckCardNumber, HINT).toInt() + val hc = ct.hintCard + insertHintCard(HintCard(cardId, hc.question, hc.hint, hc.answer)) + } + + is CT.MultiChoice -> { + val cardId = returnCard(deck, newDeckCardNumber, MULTI).toInt() + val mc = ct.multiChoiceCard + insertMultiChoiceCard( + MultiChoiceCard( + cardId, question = mc.question, + choiceA = mc.choiceA, choiceB = mc.choiceB, + choiceC = mc.choiceC, choiceD = mc.choiceD, mc.correct + ) + ) + } + + is CT.Notation -> { + val cardId = returnCard(deck, newDeckCardNumber, NOTATION).toInt() + val nc = ct.notationCard + insertNotationCard(NotationCard(cardId, nc.question, nc.steps, nc.answer)) + } + + is CT.ThreeField -> { + val cardId = returnCard(deck, newDeckCardNumber, THREE).toInt() + val tc = ct.threeFieldCard + insertThreeCard( + ThreeFieldCard(cardId, tc.question, tc.middle, tc.answer, tc.field) + ) + } + } + } + } + + /** + * First copy the selected cards to the new deck, and then delete the + * original selected cards. + */ + @Transaction + suspend fun moveCardList(cts: List, deck: Deck) { + copyCardList(cts, deck) + deleteCardList(cts) + } + + private suspend fun returnCard( + deck: Deck, newDeckCardNumber: Int, type: String + ): Long { + return insertCard( + Card( + deckId = deck.id, nextReview = Date(), passes = 0, prevSuccess = false, + totalPasses = 0, type = type, deckUUID = deck.uuid, + deckCardNumber = newDeckCardNumber, + cardIdentifier = "${deck.uuid}-$newDeckCardNumber", + reviewsLeft = deck.reviewAmount, + ) + ) + } + + private fun returnCardDeckNum(deckId: Int): Int { + return (getMaxDCNumber(deckId) ?: 0) + 1 + } } \ No newline at end of file diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/DeckDao.kt b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/DeckDao.kt index 8264413..57d0f16 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/DeckDao.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/daoInterfaces/deckAndCardDao/DeckDao.kt @@ -38,7 +38,10 @@ interface DeckDao { fun getDeck(id: Int): Deck @Query("SELECT * from decks ORDER BY name ASC") - fun getAllDecks(): Flow> + fun getAllDecksStream(): Flow> + + @Query("SELECT * from decks ORDER BY name ASC") + suspend fun getAllDecks(): List @Query("SELECT name from decks where id = :id") fun getDeckName(id: Int) : Flow diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/CardTypeRepository.kt b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/CardTypeRepository.kt index 2565e9d..38a96bd 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/CardTypeRepository.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/CardTypeRepository.kt @@ -2,6 +2,7 @@ package com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories import com.belmontCrest.cardCrafter.localDatabase.tables.BasicCard import com.belmontCrest.cardCrafter.localDatabase.tables.CT +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck import com.belmontCrest.cardCrafter.localDatabase.tables.HintCard import com.belmontCrest.cardCrafter.localDatabase.tables.MultiChoiceCard import com.belmontCrest.cardCrafter.localDatabase.tables.PartOfQorA @@ -15,9 +16,9 @@ interface CardTypeRepository { val selectedCards: StateFlow> fun toggleCard(ct: CT) - fun clearSelection() - + fun deselectAll() suspend fun toggleAllCards(deckId: Int) + val searchQuery: StateFlow fun updateQuery(query: String) fun resetQuery() @@ -44,15 +45,17 @@ interface CardTypeRepository { fun getAllCardTypesStream(deckId: Int): Flow> - fun getAllDueCards(deckId: Int, cardAmount: Int, currentTime: Long): Flow> + fun getAllDueCardsStream(deckId: Int, cardAmount: Int, currentTime: Long): Flow> - fun getDueAllCardTypes(deckId: Int, cardAmount: Int, currentTime: Long = Date().time): - List + fun getAllDueCards(deckId: Int, cardAmount: Int, currentTime: Long = Date().time): List fun getACardType(id: Int): CT fun getACardTypeStream(id: Int): Flow + suspend fun copyCardList(deck: Deck) + + suspend fun moveCardList(deck: Deck) suspend fun updateCT(cardId: Int, type: String, fields: Fields, deleteCT: CT) diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/FlashCardRepository.kt b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/FlashCardRepository.kt index 9099514..d6800f2 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/FlashCardRepository.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/FlashCardRepository.kt @@ -32,6 +32,8 @@ interface FlashCardRepository { fun getAllDecksStream(): Flow> + suspend fun getAllDecks(): List + fun getDeckStream(id: Int): Flow fun getDeck(id: Int): Deck diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineCardTypeRepository.kt b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineCardTypeRepository.kt index 9833131..b7eb4a7 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineCardTypeRepository.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineCardTypeRepository.kt @@ -10,6 +10,7 @@ import com.belmontCrest.cardCrafter.localDatabase.dbInterface.daoInterfaces.allC import com.belmontCrest.cardCrafter.localDatabase.dbInterface.daoInterfaces.deckAndCardDao.CardTypesDao import com.belmontCrest.cardCrafter.localDatabase.tables.BasicCard import com.belmontCrest.cardCrafter.localDatabase.tables.CT +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck import com.belmontCrest.cardCrafter.localDatabase.tables.HintCard import com.belmontCrest.cardCrafter.localDatabase.tables.MultiChoiceCard import com.belmontCrest.cardCrafter.localDatabase.tables.PartOfQorA @@ -61,17 +62,13 @@ class OfflineCardTypeRepository( } } - override fun clearSelection() { - _selectedCards.update { emptyList() } - } + override fun deselectAll() = _selectedCards.update { emptyList() } - override fun updateQuery(query: String) { - _searchQuery.update { query } - } - override fun resetQuery() { - _searchQuery.update { "" } - } + override fun updateQuery(query: String) = _searchQuery.update { query } + + + override fun resetQuery() = _searchQuery.update { "" } override suspend fun deleteBasicCard(basicCard: BasicCard) = basicCardDao.deleteBasicCard(basicCard) @@ -89,28 +86,16 @@ class OfflineCardTypeRepository( basicCardDao.updateBasicCard(id, question, answer) override suspend fun updateThreeCard( - id: Int, - question: String, - middle: String, - answer: String, - isQOrA: PartOfQorA + id: Int, question: String, middle: String, answer: String, isQOrA: PartOfQorA ) = threeCardDao.updateThreeCard(id, question, middle, answer, isQOrA) override suspend fun updateHintCard( - id: Int, - question: String, - hint: String, - answer: String + id: Int, question: String, hint: String, answer: String ) = hintCardDao.updateHintCard(id, question, hint, answer) override suspend fun updateMultiChoiceCard( - id: Int, - newQuestion: String, - newChoiceA: String, - newChoiceB: String, - newChoiceC: String, - newChoiceD: String, - newCorrect: Char + id: Int, newQuestion: String, newChoiceA: String, newChoiceB: String, + newChoiceC: String, newChoiceD: String, newCorrect: Char ) = multiChoiceCardDao.updateMultiChoiceCard( id, newQuestion, newChoiceA, newChoiceB, newChoiceC, newChoiceD, newCorrect ) @@ -133,10 +118,8 @@ class OfflineCardTypeRepository( listOf() } - override fun getAllDueCards( - deckId: Int, - cardAmount: Int, - currentTime: Long + override fun getAllDueCardsStream( + deckId: Int, cardAmount: Int, currentTime: Long ) = cardTypesDao.getDueAllCardTypesFlow(deckId, cardAmount, currentTime).map { try { mapAllCardTypesToCTs(it) @@ -146,7 +129,7 @@ class OfflineCardTypeRepository( } } - override fun getDueAllCardTypes( + override fun getAllDueCards( deckId: Int, cardAmount: Int, currentTime: Long ) = try { mapAllCardTypesToCTs(cardTypesDao.getDueAllCardTypes(deckId, cardAmount, currentTime)) @@ -171,6 +154,12 @@ class OfflineCardTypeRepository( throw e } + override suspend fun copyCardList(deck: Deck) = + cardTypesDao.copyCardList(_selectedCards.value, deck) + + override suspend fun moveCardList(deck: Deck) = + cardTypesDao.moveCardList(_selectedCards.value, deck) + override suspend fun updateCT( cardId: Int, type: String, fields: Fields, deleteCT: CT ) = cardTypesDao.updateCT(cardId, type, fields, deleteCT) diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineFlashCardRepository.kt b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineFlashCardRepository.kt index e7d0e0d..c29e858 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineFlashCardRepository.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/localDatabase/dbInterface/repositories/OfflineFlashCardRepository.kt @@ -38,7 +38,9 @@ class OfflineFlashCardRepository( throw e } - override fun getAllDecksStream(): Flow> = deckDao.getAllDecks() + override fun getAllDecksStream(): Flow> = deckDao.getAllDecksStream() + + override suspend fun getAllDecks() = deckDao.getAllDecks() override fun getDeckStream(id: Int) = deckDao.getDeckFlow(id) diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/model/TextProps.kt b/app/src/main/java/com/belmontCrest/cardCrafter/model/TextProps.kt index 9a7adf9..f7d2207 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/model/TextProps.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/model/TextProps.kt @@ -23,7 +23,7 @@ enum class TAProp { Start, Center, End, Default } /** Max lines prop */ enum class MLProp { One, Two, Three, Default } -enum class TCProp{ Button, Default } +enum class TCProp { Button, Default, Disabled } /** Defining our text props */ data class TextProps( @@ -64,50 +64,44 @@ fun FSProp.toTextProp(): TextProps = when (this) { FSProp.Default -> TextProps(fs = FSProp.Default) } -fun setFontSize(fsProp: FSProp): TextUnit { - return when (fsProp) { - FSProp.Font22 -> 22.sp - FSProp.Font20 -> 20.sp - FSProp.Font18 -> 18.sp - FSProp.Font16 -> 16.sp - FSProp.Font14 -> 14.sp - FSProp.Font12 -> 12.sp - FSProp.Font10 -> 10.sp - FSProp.Default -> TextUnit.Unspecified - } +fun setFontSize(fsProp: FSProp): TextUnit = when (fsProp) { + FSProp.Font22 -> 22.sp + FSProp.Font20 -> 20.sp + FSProp.Font18 -> 18.sp + FSProp.Font16 -> 16.sp + FSProp.Font14 -> 14.sp + FSProp.Font12 -> 12.sp + FSProp.Font10 -> 10.sp + FSProp.Default -> TextUnit.Unspecified } -fun setFontWeight(fwProp: FWProp): FontWeight { - return when (fwProp) { - FWProp.Bold -> FontWeight.Bold - FWProp.SemiBold -> FontWeight.SemiBold - FWProp.Default -> FontWeight.Normal - } + +fun setFontWeight(fwProp: FWProp): FontWeight = when (fwProp) { + FWProp.Bold -> FontWeight.Bold + FWProp.SemiBold -> FontWeight.SemiBold + FWProp.Default -> FontWeight.Normal } -fun setTextAlign(taProp: TAProp): TextAlign? { - return when (taProp) { - TAProp.Center -> TextAlign.Center - TAProp.Start -> TextAlign.Start - TAProp.End -> TextAlign.End - TAProp.Default -> null - } + +fun setTextAlign(taProp: TAProp): TextAlign? = when (taProp) { + TAProp.Center -> TextAlign.Center + TAProp.Start -> TextAlign.Start + TAProp.End -> TextAlign.End + TAProp.Default -> null } -fun setMaxLines(mlProp: MLProp): Int { - return when (mlProp) { - MLProp.One -> 1 - MLProp.Two -> 2 - MLProp.Three -> 3 - MLProp.Default -> Int.MAX_VALUE - } + +fun setMaxLines(mlProp: MLProp): Int = when (mlProp) { + MLProp.One -> 1 + MLProp.Two -> 2 + MLProp.Three -> 3 + MLProp.Default -> Int.MAX_VALUE } -fun setTextColor(tcProp: TCProp, getUIStyle : GetUIStyle): Color { - return when(tcProp) { - TCProp.Button -> getUIStyle.buttonTextColor() - TCProp.Default -> getUIStyle.titleColor() - } +fun setTextColor(tcProp: TCProp, getUIStyle: GetUIStyle): Color = when (tcProp) { + TCProp.Button -> getUIStyle.buttonTextColor() + TCProp.Default -> getUIStyle.titleColor() + TCProp.Disabled -> getUIStyle.disabledTextColor() } fun titledTextProp() = TextProps(FSProp.Font22, FWProp.Default, TAProp.Center) diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppContainer.kt b/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppContainer.kt index 421cb0f..427b8d4 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppContainer.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppContainer.kt @@ -8,6 +8,8 @@ import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.Offli import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.OfflineFlashCardRepository import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.OfflineScienceRepository import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.ScienceSpecificRepository +import com.belmontCrest.cardCrafter.navigation.KeyboardSelectionRepoImpl +import com.belmontCrest.cardCrafter.navigation.KeyboardSelectionRepository import com.belmontCrest.cardCrafter.supabase.model.daoAndRepository.repositories.CoOwnerRequestsRepository import com.belmontCrest.cardCrafter.supabase.model.daoAndRepository.repositories.CoOwnerRequestsRepositoryImpl import com.belmontCrest.cardCrafter.supabase.model.daoAndRepository.repositories.ownerRepos.ExportRepository @@ -58,6 +60,7 @@ interface AppContainer { val isOwnerOrCoOwnerRepo : IsOwnerOrCoOwnerRepo val deepLinkerRepo : DeepLinkerRepository val fpRepository : ForgotPasswordRepository + val kbRepository: KeyboardSelectionRepository } class AppDataContainer( @@ -141,4 +144,8 @@ class AppDataContainer( override val fpRepository: ForgotPasswordRepository by lazy { ForgotPasswordRepoImpl(sharedSupabase) } + + override val kbRepository: KeyboardSelectionRepository by lazy { + KeyboardSelectionRepoImpl() + } } \ No newline at end of file diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppViewModelProvider.kt b/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppViewModelProvider.kt index af343c9..d436532 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppViewModelProvider.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/model/application/AppViewModelProvider.kt @@ -36,6 +36,7 @@ object AppViewModelProvider { NavViewModel( flashCardApplication().container.flashCardRepository, flashCardApplication().container.cardTypeRepository, + flashCardApplication().container.kbRepository, this.createSavedStateHandle() ) } diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/Fields.kt b/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/Fields.kt index 27bad7c..9b15261 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/Fields.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/Fields.kt @@ -27,7 +27,6 @@ data class Fields( var newType: MutableState = mutableStateOf(""), var isQOrA: MutableState = mutableStateOf(PartOfQorA.A), var isEditing: MutableState = mutableStateOf(false) - ) : Parcelable { fun resetFields() { question.value = ""; middleField.value = ""; answer.value = "" diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/UiStates.kt b/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/UiStates.kt index fbc0e3e..4463bcd 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/UiStates.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/model/ui/UiStates.kt @@ -95,4 +95,21 @@ sealed class SelectedKeyboard { data object Answer : SelectedKeyboard() } +/** Whether the user decides to move the cards or copy it */ +@Parcelize +sealed class Decision : Parcelable { + @Parcelize + data object Move : Decision() + + @Parcelize + data object Copy : Decision() + + @Parcelize + data object Idle : Decision() +} + +@Parcelize +data class Dialogs( + val showDelete: Boolean, val showMoveCopy: Boolean, val showDuplicate: Boolean +) : Parcelable diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/navigation/KeyboardSelectionRepository.kt b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/KeyboardSelectionRepository.kt new file mode 100644 index 0000000..70f3fcf --- /dev/null +++ b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/KeyboardSelectionRepository.kt @@ -0,0 +1,66 @@ +package com.belmontCrest.cardCrafter.navigation + +import com.belmontCrest.cardCrafter.model.ui.SelectedKeyboard +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + + + +interface KeyboardSelectionRepository { + val showKatexKeyboard : StateFlow + val selectedKB : StateFlow + val resetOffset : StateFlow + + fun updateSelectedKB(selectedKeyboard: SelectedKeyboard) + fun resetSelectedKB() + fun retrieveKB(retrievedKB : SelectedKeyboard?) + fun toggleKeyboard() + fun resetOffset() + fun resetDone() + fun resetKeyboardStuff() + fun retrieveShowKB(show : Boolean) +} +class KeyboardSelectionRepoImpl : KeyboardSelectionRepository { + private val _showKatexKeyboard = MutableStateFlow(false) + override val showKatexKeyboard = _showKatexKeyboard.asStateFlow() + private val _selectedKB: MutableStateFlow = MutableStateFlow(null) + override val selectedKB = _selectedKB.asStateFlow() + private val _resetOffset = MutableStateFlow(false) + override val resetOffset = _resetOffset.asStateFlow() + + override fun updateSelectedKB(selectedKeyboard: SelectedKeyboard) { + _selectedKB.update { selectedKeyboard } + } + + override fun resetSelectedKB() { + _selectedKB.update { null } + } + + override fun retrieveKB(retrievedKB : SelectedKeyboard?) { + _selectedKB.update { retrievedKB } + } + + override fun toggleKeyboard() { + _showKatexKeyboard.update { !it } + } + + override fun resetOffset() { + _resetOffset.update { true } + } + + override fun resetDone() { + _resetOffset.update { false } + } + + /** Reset the selected keyboard to null and don't show the keyboard */ + override fun resetKeyboardStuff() { + resetSelectedKB() + _showKatexKeyboard.update { false } + } + + override fun retrieveShowKB(show: Boolean) { + _showKatexKeyboard.update { show } + } +} diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/navigation/NavViewModel.kt b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/NavViewModel.kt index 23fa28b..a2d8be3 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/navigation/NavViewModel.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/NavViewModel.kt @@ -5,12 +5,14 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.navigation.NavHostController +import com.belmontCrest.cardCrafter.controller.viewModels.ReusedFunc import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.CardTypeRepository import com.belmontCrest.cardCrafter.navigation.destinations.DeckViewDestination import com.belmontCrest.cardCrafter.navigation.destinations.MainNavDestination import com.belmontCrest.cardCrafter.navigation.destinations.SupabaseDestination import com.belmontCrest.cardCrafter.localDatabase.dbInterface.repositories.FlashCardRepository import com.belmontCrest.cardCrafter.localDatabase.tables.Card +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck import com.belmontCrest.cardCrafter.model.ui.StringVar import com.belmontCrest.cardCrafter.model.ui.SelectedCard import com.belmontCrest.cardCrafter.model.ui.SelectedKeyboard @@ -39,6 +41,7 @@ import kotlinx.serialization.json.Json class NavViewModel( private val flashCardRepository: FlashCardRepository, private val cardTypeRepository: CardTypeRepository, + private val kbRepository: KeyboardSelectionRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { companion object { @@ -50,15 +53,13 @@ class NavViewModel( private const val IS_SELECTING = "is_selecting" } + private val rf = ReusedFunc(flashCardRepository) private val deckId = MutableStateFlow(savedStateHandle["id"] ?: 0) private val cardId = MutableStateFlow(savedStateHandle["cardId"] ?: 0) val deckName = deckId.flatMapLatest { id -> - if (id == 0) { - flowOf(StringVar()) - } else { - flashCardRepository.getDeckName(id).map { StringVar(it ?: "") } - } + if (id == 0) flowOf(StringVar()) + else flashCardRepository.getDeckName(id).map { StringVar(it ?: "") } }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS), @@ -67,15 +68,11 @@ class NavViewModel( private val _deckNav: MutableStateFlow = MutableStateFlow(null) val deckNav = _deckNav.asStateFlow() - fun updateDeckNav(navHostController: NavHostController) { - _deckNav.update { navHostController } - } + fun updateDeckNav(navHostController: NavHostController) = _deckNav.update { navHostController } private val _sbNav: MutableStateFlow = MutableStateFlow(null) val sbNav = _sbNav.asStateFlow() - fun updateSBNav(navHostController: NavHostController) { - _sbNav.update { navHostController } - } + fun updateSBNav(navHostController: NavHostController) = _sbNav.update { navHostController } private val _route = MutableStateFlow( StringVar(savedStateHandle["route"] ?: MainNavDestination.route) @@ -108,11 +105,8 @@ class NavViewModel( } val wd: StateFlow = deckId.flatMapLatest { id -> - if (id == 0) { - flowOf(WhichDeck()) - } else { - flashCardRepository.getDeckStream(id).map { WhichDeck(it) } - } + if (id == 0) flowOf(WhichDeck()) + else flashCardRepository.getDeckStream(id).map { WhichDeck(it) } }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS), @@ -127,23 +121,16 @@ class NavViewModel( } val card = cardId.flatMapLatest { id -> - if (id == 0) { - flowOf(SelectedCard(null)) - } else { - cardTypeRepository.getACardTypeStream(id).map { - Log.d("CARD STATUS", "$it") - SelectedCard(it) - } + if (id == 0) flowOf(SelectedCard(null)) + else cardTypeRepository.getACardTypeStream(id).map { + Log.d("CARD STATUS", "$it"); SelectedCard(it) } }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS), initialValue = - if (cardId.value == 0) { - SelectedCard(null) - } else { - SelectedCard(cardTypeRepository.getACardType(cardId.value)) - } + if (cardId.value == 0) SelectedCard(null) + else SelectedCard(cardTypeRepository.getACardType(cardId.value)) ) fun getDeckById(id: Int) { @@ -151,14 +138,13 @@ class NavViewModel( deckId.update { id } } - fun deleteCard(card: Card) { - viewModelScope.launch { flashCardRepository.deleteCard(card) } - } + fun deleteCard(card: Card) = viewModelScope.launch { flashCardRepository.deleteCard(card) } suspend fun deleteCardList(): Boolean { return withContext(Dispatchers.IO) { try { cardTypeRepository.deleteCTs() + deselectAll() return@withContext true } catch (e: Exception) { Log.e(NAV_VM, "Error deleting card list: $e") @@ -167,6 +153,38 @@ class NavViewModel( } } + suspend fun copyCardList(deckId: Int): Pair { + return withContext(Dispatchers.IO) { + val deck = flashCardRepository.getDeck(deckId) + try { + val size = cardTypeRepository.selectedCards.value.size + cardTypeRepository.copyCardList(deck) + rf.updateCardsLeft(deck, size) + deselectAll() + return@withContext Pair(true, deck.name) + } catch (e: Exception) { + Log.e(NAV_VM, "Error copying card list: $e") + return@withContext Pair(false, deck.name) + } + } + } + + suspend fun moveCardList(deckId: Int): Pair { + return withContext(Dispatchers.IO) { + val deck = flashCardRepository.getDeck(deckId) + try { + val size = cardTypeRepository.selectedCards.value.size + cardTypeRepository.moveCardList(deck) + rf.updateCardsLeft(deck, size) + deselectAll() + return@withContext Pair(true, deck.name) + } catch (e: Exception) { + Log.e(NAV_VM, "Error moving card list: $e") + return@withContext Pair(false, deck.name) + } + } + } + fun getCardById(id: Int) { savedStateHandle["cardId"] = id cardId.update { id } @@ -180,81 +198,78 @@ class NavViewModel( /** Value to check is the user is syncing the deck */ private val _isBlocking = MutableStateFlow(false) val isBlocking = _isBlocking.asStateFlow() - fun updateIsBlocking() { - _isBlocking.update { true } - } - fun resetIsBlocking() { - _isBlocking.update { false } - } + fun updateIsBlocking() = _isBlocking.update { true } - private val _showKatexKeyboard: MutableStateFlow = - MutableStateFlow(savedStateHandle[SHOW_KB] as Boolean? == true) - val showKatexKeyboard = _showKatexKeyboard.asStateFlow() - private val _selectedKB: MutableStateFlow = MutableStateFlow(null) - val selectedKB = _selectedKB.asStateFlow() - private val _resetOffset = MutableStateFlow(false) - val resetOffset = _resetOffset.asStateFlow() + fun resetIsBlocking() = _isBlocking.update { false } + + val showKatexKeyboard = kbRepository.showKatexKeyboard.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS), + initialValue = savedStateHandle[SHOW_KB] as Boolean? == true + ) + val selectedKB = kbRepository.selectedKB + val resetOffset = kbRepository.resetOffset + val selectable = cardTypeRepository.selectedCards.map { it.isNotEmpty() }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS), + initialValue = false + ) fun updateSelectedKB(selectedKeyboard: SelectedKeyboard) { + kbRepository.updateSelectedKB(selectedKeyboard) savedStateHandle[KEY_SELECTED_KB] = Json.encodeToString(SelectedKeyboard.serializer(), selectedKeyboard) - _selectedKB.update { selectedKeyboard } } - fun resetSelectedKB() { - _selectedKB.update { null } - } + fun resetSelectedKB() = kbRepository.resetSelectedKB() fun retrieveKB() { - _selectedKB.update { - savedStateHandle.get(KEY_SELECTED_KB) - ?.let { Json.decodeFromString(SelectedKeyboard.serializer(), it) } - } + val kb = savedStateHandle.get(KEY_SELECTED_KB) + ?.let { Json.decodeFromString(SelectedKeyboard.serializer(), it) } + kbRepository.retrieveKB(kb) } fun toggleKeyboard() { - _showKatexKeyboard.update { savedStateHandle[SHOW_KB] = !it; !it } + savedStateHandle[SHOW_KB] = !showKatexKeyboard.value + kbRepository.toggleKeyboard() } - fun resetOffset() { - _resetOffset.update { true } - } + fun resetOffset() = kbRepository.resetOffset() - fun resetDone() { - _resetOffset.update { false } - } + fun resetDone() = kbRepository.resetDone() /** Reset the selected keyboard to null and don't show the keyboard */ fun resetKeyboardStuff() { - resetSelectedKB() + kbRepository.resetKeyboardStuff() savedStateHandle[SHOW_KB] = false - _showKatexKeyboard.update { false } } - private val _isSelecting = MutableStateFlow(savedStateHandle[IS_SELECTING] as Boolean? == true) + private val _isSelecting = + MutableStateFlow(savedStateHandle[IS_SELECTING] as Boolean? == true) val isSelecting = _isSelecting.asStateFlow() - fun isSelectingTrue() { - _isSelecting.update { true } - } + fun isSelectingTrue() = _isSelecting.update { true } - fun selectAll() { - wd.value.deck?.let { deck -> - viewModelScope.launch { cardTypeRepository.toggleAllCards(deck.id) } - } + fun selectAll() = wd.value.deck?.let { deck -> + viewModelScope.launch { cardTypeRepository.toggleAllCards(deck.id) } } - fun clearSelection() { - cardTypeRepository.clearSelection() - } + fun deselectAll() = cardTypeRepository.deselectAll() - fun resetSearchQuery() { - cardTypeRepository.resetQuery() - } + fun resetSearchQuery() = cardTypeRepository.resetQuery() fun resetSelection() { - cardTypeRepository.clearSelection() + cardTypeRepository.deselectAll() _isSelecting.update { false } } + + suspend fun getAllDecks(): List = withContext(Dispatchers.IO) { + flashCardRepository.getAllDecks() + } + + init { + val show = savedStateHandle[SHOW_KB] as Boolean? == true + kbRepository.retrieveShowKB(show) + } } \ No newline at end of file diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/ActionsIconButton.kt b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/ActionsIconButton.kt index b98cc60..ece7ed6 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/ActionsIconButton.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/ActionsIconButton.kt @@ -11,6 +11,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material3.Icon import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope @@ -31,6 +32,9 @@ import com.belmontCrest.cardCrafter.navigation.NavViewModel import com.belmontCrest.cardCrafter.navigation.destinations.ViewAllCardsDestination import com.belmontCrest.cardCrafter.navigation.destinations.ViewDueCardsDestination import com.belmontCrest.cardCrafter.controller.viewModels.cardViewsModels.CardDeckViewModel +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck +import com.belmontCrest.cardCrafter.model.ui.Decision +import com.belmontCrest.cardCrafter.model.ui.Dialogs import com.belmontCrest.cardCrafter.model.ui.Fields import com.belmontCrest.cardCrafter.navigation.destinations.AddDeckDestination import com.belmontCrest.cardCrafter.navigation.destinations.CoOwnerRequestsDestination @@ -77,7 +81,8 @@ fun ActionIconButton( navViewModel.updateRoute(ViewAllCardsDestination.route) deckNavController?.navigate(ViewAllCardsDestination.route) } - val cr = navViewModel.route.collectAsStateWithLifecycle().value + val cr by navViewModel.route.collectAsStateWithLifecycle() + val selectable by navViewModel.selectable.collectAsStateWithLifecycle() when (cr.name) { MainNavDestination.route -> { @@ -130,6 +135,10 @@ fun ActionIconButton( } ViewAllCardsDestination.route -> { + var deckList by rememberSaveable { mutableStateOf(emptyList()) } + LaunchedEffect(Unit) { + coroutineScope.launch { deckList = navViewModel.getAllDecks() } + } if (!isSelecting) { BackButton( onBackClick = { @@ -144,23 +153,78 @@ fun ActionIconButton( ) } else { var enabled by rememberSaveable { mutableStateOf(true) } - var showDialog by rememberSaveable { mutableStateOf(false) } + var showDelDialog by rememberSaveable { mutableStateOf(false) } + var showCORMDialog by rememberSaveable { mutableStateOf(false) } + var showDupDialog by rememberSaveable { mutableStateOf(false) } val expanded = rememberSaveable { mutableStateOf(false) } + val onFinished: () -> Unit = { + showCORMDialog = false; enabled = true; expanded.value = false + showDupDialog = false; showDelDialog = false + } CardListOptions( onDelete = { coroutineScope.launch { enabled = false val success = navViewModel.deleteCardList() if (!success) showToastMessage(context, "Failed to delete cards") - showDialog = false - enabled = true - expanded.value = false + onFinished() + } + }, + onDuplicate = { + wd.deck?.let { deck -> + coroutineScope.launch { + enabled = false + val (success, _) = navViewModel.copyCardList(deck.id) + if (!success) showToastMessage(context, "Failed to duplicate cards") + else showToastMessage(context, "Duplicated cards successfully!") + onFinished() + } } }, + onCopyMoveCL = { decision, deckId -> + when (decision) { + Decision.Copy -> { + coroutineScope.launch { + enabled = false + val (success, deckName) = navViewModel.copyCardList(deckId) + if (!success) showToastMessage( + context, "Failed to copy cards to $$**", deckName + ) + else showToastMessage( + context, "Copied cards to $$**!", deckName + ) + onFinished() + } + } + + Decision.Idle -> { + showToastMessage(context, "Error: no valid decision made.") + } + + Decision.Move -> { + coroutineScope.launch { + enabled = false + val (success, deckName) = navViewModel.moveCardList(deckId) + if (!success) showToastMessage( + context, "Failed to move cards to $$**", deckName + ) + else showToastMessage( + context, "Moved cards to $$**!", deckName + ) + onFinished() + } + } + } + }, deckList = deckList, selectedDeck = wd.deck, getUIStyle = getUIStyle, onSelectAll = { coroutineScope.launch { navViewModel.selectAll() } }, - onClearSelection = { navViewModel.clearSelection() }, - getUIStyle, enabled = enabled, showDialog = showDialog, - onDialogToggle = { showDialog = it }, expanded = expanded + onDeselectAll = { navViewModel.deselectAll() }, selectable = selectable, + onCORMDialogToggle = { showCORMDialog = it }, expanded = expanded, + onDelDialogToggle = { showDelDialog = it }, enabled = enabled, + onDupDialogToggle = { showDupDialog = it }, + dialogs = Dialogs( + showDelete = showDelDialog, showMoveCopy = showCORMDialog, + showDuplicate = showDupDialog + ) ) } } diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/CustomNavigationDrawer.kt b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/CustomNavigationDrawer.kt index 3fc95f6..2f2a365 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/CustomNavigationDrawer.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/navigation/drawer/CustomNavigationDrawer.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController @@ -133,9 +134,9 @@ fun CustomNavigationDrawer( title = { if (titleText == ExportSBDestination.route) { Text( - text = titleText, - color = getUIStyle.titleColor(), + text = titleText, color = getUIStyle.titleColor(), textAlign = TextAlign.End, + maxLines = 2, overflow = TextOverflow.Ellipsis, style = MaterialTheme.typography.bodyMedium, fontSize = 22.sp, modifier = Modifier @@ -144,9 +145,9 @@ fun CustomNavigationDrawer( ) } else { Text( - text = titleText, - color = getUIStyle.titleColor(), + text = titleText, color = getUIStyle.titleColor(), textAlign = TextAlign.Start, + maxLines = 2, overflow = TextOverflow.Ellipsis, fontSize = 22.sp, modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/Color.kt b/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/Color.kt index a3948de..bc6324f 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/Color.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/Color.kt @@ -7,6 +7,8 @@ val dialogLightBackground = Color(136, 182, 224, 240) val backgroundColor = Color(212, 241, 244) val secondaryBC = Color(197, 243, 246, 255) val onBackgroundColor = Color(50, 50, 200) +val lightDisabled = Color(59, 59, 59, 204) + val editingBackGroundColor = Color(190, 240, 255) val semiTransBG = Color(198, 236, 250, 217) val semiTransHeader = Color(167, 212, 225, 217) @@ -32,6 +34,8 @@ val dialogDarkBackground = Color(0, 0, 44, 240) val darkBackground = Color(20,20,25) val secondaryDBC = Color(19, 19, 38, 255) val darkOnBackground = Color(224, 224, 224) +val darkDisabled = Color(115, 115, 115, 204) + val darkEditingBackground = Color(28, 28, 68) val darkSTBG = Color(22, 22, 56, 217) val darkSTHeader = Color(16, 16, 42, 217) diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/GetUIStyle.kt b/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/GetUIStyle.kt index d908ff2..d67aa5a 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/GetUIStyle.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/ui/theme/GetUIStyle.kt @@ -12,231 +12,183 @@ class GetUIStyle( private var isDynamicTheme: Boolean, private var isCuteTheme: Boolean, ) { - fun getIsDarkTheme(): Boolean { - return isDarkTheme - } + fun getIsDarkTheme(): Boolean = isDarkTheme - fun getIsCuteTheme(): Boolean { - return isCuteTheme - } + fun getIsCuteTheme(): Boolean = isCuteTheme /** if (isDarkTheme && isCuteTheme) return true */ - private fun ifDarkAndCute(): Boolean { - return isDarkTheme && isCuteTheme - } + private fun ifDarkAndCute(): Boolean = isDarkTheme && isCuteTheme /** if (isDarkTheme && !isCuteTheme) return true */ - private fun ifDarkAndNotCute(): Boolean { - return isDarkTheme && !isCuteTheme - } + private fun ifDarkAndNotCute(): Boolean = isDarkTheme && !isCuteTheme /** if (!isDarkTheme && isCuteTheme) return true */ - private fun ifNotDarkAndCute(): Boolean { - return !isDarkTheme && isCuteTheme - } + private fun ifNotDarkAndCute(): Boolean = !isDarkTheme && isCuteTheme - fun getColorScheme(): ColorScheme { - return cS.colorScheme - } + fun getColorScheme(): ColorScheme = cS.colorScheme - fun buttonColor(): Color { - return cS.colorScheme.primaryContainer - } + fun buttonColor(): Color = cS.colorScheme.primaryContainer - fun semiTransButtonColor(): Color { - return when { - ifDarkAndCute() -> semiTransDarkCuteButton - ifDarkAndNotCute() -> semiTransDarkButton - ifNotDarkAndCute() -> semiTransCuteButton - else -> semiTransButtonColor - } + fun semiTransButtonColor(): Color = when { + ifDarkAndCute() -> semiTransDarkCuteButton + ifDarkAndNotCute() -> semiTransDarkButton + ifNotDarkAndCute() -> semiTransCuteButton + else -> semiTransButtonColor } - fun iconColor(): Color { - return cS.colorScheme.onPrimaryContainer - } + fun iconColor(): Color = cS.colorScheme.onPrimaryContainer - fun secondaryButtonColor(): Color { - return cS.colorScheme.secondaryContainer - } + fun secondaryButtonColor(): Color = cS.colorScheme.secondaryContainer - fun buttonTextColor(): Color { - return cS.colorScheme.onSecondaryContainer - } + fun buttonTextColor(): Color = cS.colorScheme.onSecondaryContainer - fun titleColor(): Color { - return cS.colorScheme.onBackground - } + fun titleColor(): Color = cS.colorScheme.onBackground - fun tertiaryButtonColor(): Color { - return cS.colorScheme.tertiaryContainer - } - fun onTertiaryButtonColor(): Color { - return cS.colorScheme.onTertiaryContainer - } + fun disabledTextColor(): Color = if (isDarkTheme) darkDisabled else lightDisabled + + fun tertiaryButtonColor(): Color = cS.colorScheme.tertiaryContainer + + fun onTertiaryButtonColor(): Color = cS.colorScheme.onTertiaryContainer - fun choiceColor(): Color { - return when { - isDarkTheme -> { - if (isDynamicTheme) { - blendColors(cS.colorScheme.onTertiary, darkChoiceColor, 0.25f) + fun choiceColor(): Color = when { + isDarkTheme -> { + if (isDynamicTheme) { + blendColors(cS.colorScheme.onTertiary, darkChoiceColor, 0.25f) + } else { + if (isCuteTheme) { + darkCuteChoiceColor } else { - if (isCuteTheme) { - darkCuteChoiceColor - } else { - darkChoiceColor - } + darkChoiceColor } } + } - else -> { - if (isDynamicTheme) { - blendColors(cS.colorScheme.onTertiary, choiceColor, 0.25f) + else -> { + if (isDynamicTheme) { + blendColors(cS.colorScheme.onTertiary, choiceColor, 0.25f) + } else { + if (isCuteTheme) { + cuteChoiceColor } else { - if (isCuteTheme) { - cuteChoiceColor - } else { - choiceColor - } + choiceColor } } } } - fun pickedChoice(): Color { - return when { - ifDarkAndCute() -> darkCutePickedChoice - ifDarkAndNotCute() -> darkPickedChoice - ifNotDarkAndCute() -> cutePickedChoice - else -> pickedChoice - } + fun pickedChoice(): Color = when { + ifDarkAndCute() -> darkCutePickedChoice + ifDarkAndNotCute() -> darkPickedChoice + ifNotDarkAndCute() -> cutePickedChoice + else -> pickedChoice } - fun correctChoice(): Color { - return when { - ifDarkAndCute() -> darkCuteCorrectChoice - ifDarkAndNotCute() -> darkCorrectChoice - ifNotDarkAndCute() -> cuteCorrectChoice - else -> correctChoice - } + fun correctChoice(): Color = when { + ifDarkAndCute() -> darkCuteCorrectChoice + ifDarkAndNotCute() -> darkCorrectChoice + ifNotDarkAndCute() -> cuteCorrectChoice + else -> correctChoice } - fun onCorrectChoice(): Color { - return when { - ifDarkAndCute() -> onDarkCuteCorrectChoice - ifNotDarkAndCute() -> onCuteCorrectChoice - else -> cS.colorScheme.onSurfaceVariant - } + + fun onCorrectChoice(): Color = when { + ifDarkAndCute() -> onDarkCuteCorrectChoice + ifNotDarkAndCute() -> onCuteCorrectChoice + else -> cS.colorScheme.onSurfaceVariant } - fun isThemeOn(): Color { - return if (isDarkTheme) { - Color.White - } else { - darkBackground - } + fun isThemeOn(): Color = if (isDarkTheme) { + Color.White + } else { + darkBackground } - fun altBackground(): Color { - return when { - isDarkTheme -> { - if (isCuteTheme) { - darkCuteSecondaryBC - } else { - secondaryDBC - } + fun altBackground(): Color = when { + isDarkTheme -> { + if (isCuteTheme) { + darkCuteSecondaryBC + } else { + secondaryDBC } + } - else -> { - if (isCuteTheme) { - cuteSecondaryBC - } else { - secondaryBC - } + else -> { + if (isCuteTheme) { + cuteSecondaryBC + } else { + secondaryBC } } } - fun background(): Color { - return cS.colorScheme.background - } + fun background(): Color = cS.colorScheme.background - fun navBarColor(): Color { - return when { - isDarkTheme -> { - if (!isDynamicTheme) { - if (isCuteTheme) { - darkCuteButton - } else { - darkNavBar - } + fun navBarColor(): Color = when { + isDarkTheme -> { + if (!isDynamicTheme) { + if (isCuteTheme) { + darkCuteButton } else { - cS.colorScheme.primaryContainer + darkNavBar } + } else { + cS.colorScheme.primaryContainer } + } - else -> { - if (isCuteTheme && !isDynamicTheme) { - cuteButton - } else { - cS.colorScheme.primaryContainer - } + else -> { + if (isCuteTheme && !isDynamicTheme) { + cuteButton + } else { + cS.colorScheme.primaryContainer } } } - fun dialogColor(): Color { - return when { - ifDarkAndCute() -> Color(70, 20, 50, 240) - ifDarkAndNotCute() -> dialogDarkBackground - ifNotDarkAndCute() -> Color(255, 230, 240, 240) - else -> dialogLightBackground - } + fun dialogColor(): Color = when { + ifDarkAndCute() -> Color(70, 20, 50, 240) + ifDarkAndNotCute() -> dialogDarkBackground + ifNotDarkAndCute() -> Color(255, 230, 240, 240) + else -> dialogLightBackground } - fun importingDeckColor(): Color { - return when { - isDarkTheme -> { - if (isCuteTheme) { - Color(100, 40, 80) - } else { - Color.DarkGray - } + fun importingDeckColor(): Color = when { + isDarkTheme -> { + if (isCuteTheme) { + Color(100, 40, 80) + } else { + Color.DarkGray } + } - else -> { - if (isCuteTheme) { - Color(255, 192, 203, 180) - } else { - Color.Gray - } + else -> { + if (isCuteTheme) { + Color(255, 192, 203, 180) + } else { + Color.Gray } } } - fun defaultIconColor(): Color { - return if (isDarkTheme) { - Color.White - } else { - Color.Black - } + fun defaultIconColor(): Color = if (isDarkTheme) { + Color.White + } else { + Color.Black } - fun katexMenuBGColor(): Color { - return when { - ifDarkAndCute() -> darkCuteSTBG - ifDarkAndNotCute() -> darkSTBG - ifNotDarkAndCute() -> cuteSTBG - else -> semiTransBG - } + + fun katexMenuBGColor(): Color = when { + ifDarkAndCute() -> darkCuteSTBG + ifDarkAndNotCute() -> darkSTBG + ifNotDarkAndCute() -> cuteSTBG + else -> semiTransBG } - fun katexMenuHeaderColor(): Color { - return when { - ifDarkAndCute() -> darkCuteSTHeader - ifDarkAndNotCute() -> darkSTHeader - ifNotDarkAndCute() -> cuteSTHeader - else -> semiTransHeader - } + + fun katexMenuHeaderColor(): Color = when { + ifDarkAndCute() -> darkCuteSTHeader + ifDarkAndNotCute() -> darkSTHeader + ifNotDarkAndCute() -> cuteSTHeader + else -> semiTransHeader } fun blendColors(base: Color, overlay: Color, overlayAlpha: Float): Color { diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/ShowToastMessage.kt b/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/ShowToastMessage.kt index 56c905f..e6c4600 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/ShowToastMessage.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/ShowToastMessage.kt @@ -1,15 +1,20 @@ package com.belmontCrest.cardCrafter.uiFunctions import android.content.Context +import android.graphics.Typeface +import android.text.Spannable +import android.text.SpannableString +import android.text.style.StyleSpan import android.widget.Toast import androidx.compose.runtime.MutableState +import androidx.core.text.buildSpannedString fun showToastMessage( - context : Context, - message : String, - onNavigate : (() -> Unit)? = null, - dismiss : MutableState? = null + context: Context, + message: String, + onNavigate: (() -> Unit)? = null, + dismiss: MutableState? = null ) { Toast.makeText( context, @@ -18,4 +23,27 @@ fun showToastMessage( ).show() onNavigate?.invoke() dismiss?.value = false +} + +fun showToastMessage( + context: Context, message: String, param: String, + onNavigate: (() -> Unit)? = null, dismiss: MutableState? = null +) { + val subText = message.substringBefore("$$**") + val spannable = SpannableString(param) + spannable.setSpan( + StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + val text = buildSpannedString { + append(subText) + append(spannable) + append(message.substringAfter("$$**")) + } + Toast.makeText( + context, + text, + Toast.LENGTH_SHORT + ).show() + onNavigate?.invoke() + dismiss?.value = false } \ No newline at end of file diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/Buttons.kt b/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/Buttons.kt index 4f3344f..bd9528d 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/Buttons.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/Buttons.kt @@ -146,6 +146,8 @@ fun RedoCardButton( }, modifier = modifier ) { + val ci = ContentIcons(getUIStyle) +ci.ContentIcon() Icon( painter = painterResource(R.drawable.return_arrow), modifier = Modifier @@ -166,6 +168,7 @@ fun SettingsButton( fields: Fields ) { var expanded by remember { mutableStateOf(false) } + val ci = ContentIcons(getUIStyle) IconButton( onClick = { if (!fields.inDeckClicked.value) { @@ -179,13 +182,7 @@ fun SettingsButton( ) .padding(6.dp) ) { - Icon( - imageVector = Icons.Filled.Edit, - modifier = Modifier - .size(24.dp), - contentDescription = "Settings", - tint = getUIStyle.iconColor() - ) + ci.ContentIcon("Settings", Icons.Filled.Edit, Modifier.size(24.dp)) DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { DropdownMenuItem( onClick = { diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/CardOptionsButtons.kt b/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/CardOptionsButtons.kt index a47e96e..b0b5b15 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/CardOptionsButtons.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/uiFunctions/buttons/CardOptionsButtons.kt @@ -35,10 +35,15 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.belmontCrest.cardCrafter.R +import com.belmontCrest.cardCrafter.controller.onClickActions.CopyMoveCardList import com.belmontCrest.cardCrafter.controller.onClickActions.DeleteCard import com.belmontCrest.cardCrafter.controller.onClickActions.DeleteCards +import com.belmontCrest.cardCrafter.controller.onClickActions.DuplicateCards import com.belmontCrest.cardCrafter.localDatabase.tables.Card +import com.belmontCrest.cardCrafter.localDatabase.tables.Deck import com.belmontCrest.cardCrafter.model.Type +import com.belmontCrest.cardCrafter.model.ui.Decision +import com.belmontCrest.cardCrafter.model.ui.Dialogs import com.belmontCrest.cardCrafter.model.ui.Fields import com.belmontCrest.cardCrafter.navigation.NavViewModel import com.belmontCrest.cardCrafter.ui.theme.GetUIStyle @@ -152,15 +157,15 @@ fun CardTypesButton(getUIStyle: GetUIStyle, navVM: NavViewModel) { Icon( Icons.Default.MoreVert, contentDescription = "Card Type", - tint = getUIStyle.titleColor() + tint = getUIStyle.defaultIconColor() ) } DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) { CardItems( - toBasic = { navVM.updateType(Type.BASIC) }, - toThree = { navVM.updateType(Type.THREE) }, - toHint = { navVM.updateType(Type.HINT) }, - toMulti = { navVM.updateType(Type.MULTI) }, + toBasic = { navVM.updateType(Type.BASIC); navVM.resetKeyboardStuff() }, + toThree = { navVM.updateType(Type.THREE); navVM.resetKeyboardStuff() }, + toHint = { navVM.updateType(Type.HINT); navVM.resetKeyboardStuff() }, + toMulti = { navVM.updateType(Type.MULTI); navVM.resetKeyboardStuff() }, toNotation = { navVM.updateType(Type.NOTATION) } ) } @@ -221,13 +226,13 @@ fun ToggleKeyBoard( Icon( painterResource(R.drawable.twotone_keyboard), contentDescription = "Keyboard", - tint = getUIStyle.titleColor() + tint = getUIStyle.defaultIconColor() ) } else { Icon( painterResource(R.drawable.twotone_keyboard_hide), contentDescription = "Hide Keyboard", - tint = getUIStyle.titleColor() + tint = getUIStyle.defaultIconColor() ) } } @@ -236,15 +241,14 @@ fun ToggleKeyBoard( @Composable fun CardListOptions( - onDelete: () -> Unit, - onSelectAll: () -> Unit, - onClearSelection: () -> Unit, - getUIStyle: GetUIStyle, - enabled: Boolean, - showDialog: Boolean, - onDialogToggle: (Boolean) -> Unit, - expanded: MutableState + onDelete: () -> Unit, onCopyMoveCL: (Decision, Int) -> Unit, + onSelectAll: () -> Unit, onDeselectAll: () -> Unit, onDuplicate: () -> Unit, + getUIStyle: GetUIStyle, enabled: Boolean, dialogs: Dialogs, + onDelDialogToggle: (Boolean) -> Unit, expanded: MutableState, + selectedDeck: Deck?, onCORMDialogToggle: (Boolean) -> Unit, selectable: Boolean, + onDupDialogToggle: (Boolean) -> Unit, deckList: List ) { + var decision by rememberSaveable { mutableStateOf(Decision.Idle) } Box(Modifier.wrapContentSize(Alignment.TopEnd)) { IconButton( onClick = { expanded.value = true }, @@ -253,37 +257,56 @@ fun CardListOptions( .size(54.dp) ) { Icon( - Icons.Default.MoreVert, contentDescription = null, - tint = getUIStyle.titleColor() + Icons.Default.MoreVert, contentDescription = null, tint = getUIStyle.titleColor() ) } DropdownMenu(expanded = expanded.value, onDismissRequest = { expanded.value = false }) { DropdownMenuItem( onClick = { onSelectAll() }, - text = { Text("Select all") }, + text = { Text(stringResource(R.string.select_all)) }, + ) + DropdownMenuItem( + onClick = { onDeselectAll() }, + text = { Text(stringResource(R.string.deselect_all)) } ) DropdownMenuItem( - onClick = { onClearSelection() }, - text = { Text("Clear selection") } + onClick = { onCORMDialogToggle(true); decision = Decision.Copy }, + enabled = selectable, text = { Text(stringResource(R.string.copy_cards)) } ) DropdownMenuItem( - onClick = { onDialogToggle(true) }, + onClick = { onCORMDialogToggle(true); decision = Decision.Move }, + enabled = selectable, text = { Text(stringResource(R.string.move_cards)) } + ) + DropdownMenuItem( + onClick = { onDupDialogToggle(true) }, enabled = selectable, + text = { Text(stringResource(R.string.duplicate_cards)) }) + DropdownMenuItem( + onClick = { onDelDialogToggle(true) }, enabled = selectable, text = { Text(stringResource(R.string.delete_card_list)) }, trailingIcon = { Icon( imageVector = Icons.Filled.Delete, modifier = Modifier .size(28.dp), - contentDescription = "Delete Card", + contentDescription = stringResource(R.string.delete_card), tint = getUIStyle.iconColor() ) } ) - } DeleteCards( - showDialog = showDialog, onDismiss = { onDialogToggle(it) }, + showDialog = dialogs.showDelete, onDismiss = { onDelDialogToggle(it) }, enabled = enabled, getUIStyle = getUIStyle, onDelete = { onDelete() } ) + CopyMoveCardList( + showDialog = dialogs.showMoveCopy, onDismiss = { onCORMDialogToggle(it) }, + enabled = enabled, getUIStyle = getUIStyle, onCopyOrMove = { id -> + if (decision !is Decision.Idle) onCopyMoveCL(decision, id) + }, deckList = deckList, selectedDeck = selectedDeck + ) + DuplicateCards( + showDialog = dialogs.showDuplicate, onDismiss = { onDupDialogToggle(it) }, + enabled = enabled, getUIStyle = getUIStyle, onDuplicate = { onDuplicate() } + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/belmontCrest/cardCrafter/views/cardViews/editCardViews/EditingCardsList.kt b/app/src/main/java/com/belmontCrest/cardCrafter/views/cardViews/editCardViews/EditingCardsList.kt index d8843d4..1d4fb93 100644 --- a/app/src/main/java/com/belmontCrest/cardCrafter/views/cardViews/editCardViews/EditingCardsList.kt +++ b/app/src/main/java/com/belmontCrest/cardCrafter/views/cardViews/editCardViews/EditingCardsList.kt @@ -101,9 +101,9 @@ class EditCardsList( modifier = Modifier.fillMaxSize(), state = listState ) { + val selectedCardIds = selectedCTL.map { it.getCardId() }.toSet() items(filtered.size, key = { index -> filtered[index].getCardId() }) { index -> - val selected = selectedCTL.any { it.getCardId() == filtered[index].getCardId() } - + val selected = filtered[index].getCardId() in selectedCardIds CardItem( filtered, index, isSelecting = isSelecting, selected = selected, onTap = { diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index af3e6c9..39f5128 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -85,7 +85,17 @@ No hay credenciales No es el propietario ni copropietario de este mazo. ¿Estás seguro de que quieres eliminar estas tarjetas? + ¿Estás seguro de que quieres duplicar estas tarjetas? + Duplicar Cartas Borrar Cartas Teclado desactivado + + Seleccionar todo + Deseleccionar todo + Copiar cartas + Mover cartas + Duplicar cartas + + OK Buscar… \ 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 08db831..98cadaf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,6 +47,7 @@ Edit Multiplier Good Multiplier Bad Multiplier + Multiple Choice Card Multiple Choices Choice A @@ -85,7 +86,17 @@ No credentials available You are not the owner or a co-owner of this deck. Are you sure you want to delete these cards? + Are you sure you want to duplicate these cards? + Duplicate Cards Delete Cards Keyboard Disabled + + Okay Search… + + Select all + Deselect all + Copy cards + Move cards + Duplicate cards \ No newline at end of file