Skip to content

Commit 3b43635

Browse files
authored
Merge pull request #552 from jorgeblacio/recovery_code
Added recovery code functionality
2 parents 5aae486 + 44ee8e5 commit 3b43635

23 files changed

+402
-22
lines changed

src/main/kotlin/com/criptext/mail/db/ComposerLocalDB.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class ComposerLocalDB(val contactDao: ContactDao, val emailDao: EmailDao, val fi
2222
val labels = emailLabelDao.getLabelsFromEmail(id)
2323
val contactsCC = emailContactDao.getContactsFromEmail(id, ContactTypes.CC)
2424
val contactsBCC = emailContactDao.getContactsFromEmail(id, ContactTypes.BCC)
25-
val contactsFROM = emailContactDao.getContactsFromEmail(id, ContactTypes.FROM)
2625
val contactsTO = emailContactDao.getContactsFromEmail(id, ContactTypes.TO)
2726
val files = fileDao.getAttachmentsFromEmail(id)
2827
val fileKey = fileKeyDao.getAttachmentKeyFromEmail(id)
@@ -65,7 +64,6 @@ class ComposerLocalDB(val contactDao: ContactDao, val emailDao: EmailDao, val fi
6564
labelDao.get(selectedLabel, activeAccount.id).id) else -1
6665
val contactsCC = emailContactDao.getContactsFromEmail(id, ContactTypes.CC)
6766
val contactsBCC = emailContactDao.getContactsFromEmail(id, ContactTypes.BCC)
68-
val contactsFROM = emailContactDao.getContactsFromEmail(id, ContactTypes.FROM)
6967
val contactsTO = emailContactDao.getContactsFromEmail(id, ContactTypes.TO)
7068
val files = fileDao.getAttachmentsFromEmail(id)
7169
val fileKey: FileKey? = fileKeyDao.getAttachmentKeyFromEmail(id)

src/main/kotlin/com/criptext/mail/db/EventLocalDB.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,6 @@ class EventLocalDB(private val db: AppDatabase, private val filesDir: File, priv
317317
val labels = db.emailLabelDao().getLabelsFromEmail(id)
318318
val contactsCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.CC)
319319
val contactsBCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.BCC)
320-
val contactsFROM = db.emailContactDao().getContactsFromEmail(id, ContactTypes.FROM)
321320
val contactsTO = db.emailContactDao().getContactsFromEmail(id, ContactTypes.TO)
322321
val files = db.fileDao().getAttachmentsFromEmail(id)
323322
val fileKey = db.fileKeyDao().getAttachmentKeyFromEmail(id)

src/main/kotlin/com/criptext/mail/db/MailboxLocalDB.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,6 @@ interface MailboxLocalDB {
475475
db.labelDao().get(selectedLabel, activeAccount.id).id) else -1
476476
val contactsCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.CC)
477477
val contactsBCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.BCC)
478-
val contactsFROM = db.emailContactDao().getContactsFromEmail(id, ContactTypes.FROM)
479478
val contactsTO = db.emailContactDao().getContactsFromEmail(id, ContactTypes.TO)
480479
val files = db.fileDao().getAttachmentsFromEmail(id)
481480
val fileKey: FileKey? = db.fileKeyDao().getAttachmentKeyFromEmail(id)

src/main/kotlin/com/criptext/mail/db/SearchLocalDB.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ interface SearchLocalDB{
5858
val labels = db.emailLabelDao().getLabelsFromEmail(id)
5959
val contactsCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.CC)
6060
val contactsBCC = db.emailContactDao().getContactsFromEmail(id, ContactTypes.BCC)
61-
val contactsFROM = db.emailContactDao().getContactsFromEmail(id, ContactTypes.FROM)
6261
val contactsTO = db.emailContactDao().getContactsFromEmail(id, ContactTypes.TO)
6362
val files = db.fileDao().getAttachmentsFromEmail(id)
6463
val fileKey = db.fileKeyDao().getAttachmentKeyFromEmail(id)

src/main/kotlin/com/criptext/mail/scenes/signin/SignInScene.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.criptext.mail.scenes.signup.holders.KeyGenerationHolder
1111
import com.criptext.mail.utils.UIMessage
1212
import com.criptext.mail.utils.getLocalizedUIMessage
1313
import com.criptext.mail.utils.ui.ForgotPasswordDialog
14+
import com.criptext.mail.utils.ui.GeneralDialogWithInput
1415
import com.criptext.mail.utils.ui.GeneralMessageOkDialog
1516
import com.criptext.mail.utils.ui.RetrySyncAlertDialogNewDevice
1617
import com.criptext.mail.utils.ui.data.DialogData
@@ -45,6 +46,10 @@ interface SignInScene {
4546
fun showDeviceCountRemaining(remaining: Int)
4647
fun showDeviceRemovalError()
4748
fun showToolbarCount(checked: Int)
49+
fun showRecoveryCode()
50+
fun showRecoveryDialogError(message: UIMessage?)
51+
fun toggleLoadRecoveryCode(load: Boolean)
52+
fun dismissRecoveryCodeDialog()
4853

4954
var signInUIObserver: SignInSceneController.SignInUIObserver?
5055

@@ -71,6 +76,14 @@ interface SignInScene {
7176
)
7277
)
7378

79+
private val recoveryCodeDialog = GeneralDialogWithInput(view.context,
80+
DialogData.DialogDataForRecoveryCode(
81+
title = UIMessage(R.string.recovery_code_dialog_title),
82+
message = UIMessage(R.string.recovery_code_dialog_message),
83+
type = DialogType.RecoveryCode()
84+
)
85+
)
86+
7487
override var signInUIObserver: SignInSceneController.SignInUIObserver? = null
7588
set(value) {
7689
holder.uiObserver = value
@@ -254,6 +267,24 @@ interface SignInScene {
254267
currentHolder.setToolbarCount(checked)
255268
}
256269

270+
override fun showRecoveryCode() {
271+
recoveryCodeDialog.showDialog(signInUIObserver)
272+
recoveryCodeDialog.editTextEmail.setHint(R.string.recovery_code_dialog_hint)
273+
recoveryCodeDialog.editTextEmailLayout.hint = view.context.getLocalizedUIMessage(UIMessage(R.string.recovery_code_dialog_hint))
274+
}
275+
276+
override fun showRecoveryDialogError(message: UIMessage?) {
277+
recoveryCodeDialog.setEmailError(message)
278+
}
279+
280+
override fun toggleLoadRecoveryCode(load: Boolean) {
281+
recoveryCodeDialog.toggleLoad(load)
282+
}
283+
284+
override fun dismissRecoveryCodeDialog() {
285+
recoveryCodeDialog.dismiss()
286+
}
287+
257288
override fun showKeyGenerationHolder() {
258289
viewGroup.removeAllViews()
259290
val keyGenerationLayout = View.inflate(

src/main/kotlin/com/criptext/mail/scenes/signin/SignInSceneController.kt

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import com.criptext.mail.utils.*
2424
import com.criptext.mail.utils.generaldatasource.data.GeneralDataSource
2525
import com.criptext.mail.utils.generaldatasource.data.GeneralRequest
2626
import com.criptext.mail.utils.generaldatasource.data.GeneralResult
27+
import com.criptext.mail.utils.ui.data.DialogResult
28+
import com.criptext.mail.utils.ui.data.DialogType
29+
import com.criptext.mail.utils.uiobserver.UIObserver
2730
import com.criptext.mail.utils.virtuallist.VirtualListView
2831
import com.criptext.mail.validation.AccountDataValidator
2932
import com.criptext.mail.validation.FormData
@@ -67,6 +70,7 @@ class SignInSceneController(
6770
is SignInResult.LinkStatus -> onLinkStatus(result)
6871
is SignInResult.FindDevices -> onFindDevices(result)
6972
is SignInResult.RemoveDevices -> onRemoveDevices(result)
73+
is SignInResult.RecoveryCode -> onGenerateRecoveryCode(result)
7074
}
7175
}
7276

@@ -391,6 +395,27 @@ class SignInSceneController(
391395
}
392396
}
393397

398+
private fun onGenerateRecoveryCode(result: SignInResult.RecoveryCode){
399+
when(result){
400+
is SignInResult.RecoveryCode.Success -> {
401+
if(result.isValidate) {
402+
scene.dismissRecoveryCodeDialog()
403+
scene.showKeyGenerationHolder()
404+
} else {
405+
scene.showRecoveryCode()
406+
}
407+
}
408+
is SignInResult.RecoveryCode.Failure -> {
409+
if(result.isValidate){
410+
scene.toggleLoadRecoveryCode(false)
411+
scene.showRecoveryDialogError(result.message)
412+
} else {
413+
scene.showError(result.message)
414+
}
415+
}
416+
}
417+
}
418+
394419
private fun onUserAuthenticated(result: SignInResult.AuthenticateUser) {
395420
when (result) {
396421
is SignInResult.AuthenticateUser.Success -> {
@@ -622,6 +647,51 @@ class SignInSceneController(
622647
}
623648

624649
private val uiObserver = object : SignInUIObserver {
650+
override fun onRecoveryCodeChangeListener(newPassword: String) {
651+
652+
}
653+
654+
override fun onGeneralOkButtonPressed(result: DialogResult) {
655+
if(result is DialogResult.DialogWithInput && result.type is DialogType.RecoveryCode){
656+
scene.toggleLoadRecoveryCode(true)
657+
val currentState = model.state as SignInLayoutState.LoginValidation
658+
dataSource.submitRequest(SignInRequest.RecoveryCode(currentState.username, currentState.domain, model.ephemeralJwt, model.isMultiple, result.textInput))
659+
}
660+
}
661+
662+
override fun onOkButtonPressed(password: String) {
663+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
664+
}
665+
666+
override fun onCancelButtonPressed() {
667+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
668+
}
669+
670+
override fun onLinkAuthConfirmed(untrustedDeviceInfo: DeviceInfo.UntrustedDeviceInfo) {
671+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
672+
}
673+
674+
override fun onLinkAuthDenied(untrustedDeviceInfo: DeviceInfo.UntrustedDeviceInfo) {
675+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
676+
}
677+
678+
override fun onSnackbarClicked() {
679+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
680+
}
681+
682+
override fun onSyncAuthConfirmed(trustedDeviceInfo: DeviceInfo.TrustedDeviceInfo) {
683+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
684+
}
685+
686+
override fun onSyncAuthDenied(trustedDeviceInfo: DeviceInfo.TrustedDeviceInfo) {
687+
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
688+
}
689+
690+
override fun onRecoveryCodeClicked() {
691+
val currentState = model.state as SignInLayoutState.LoginValidation
692+
dataSource.submitRequest(SignInRequest.RecoveryCode(currentState.username, currentState.domain, model.ephemeralJwt, model.isMultiple))
693+
}
694+
625695
override fun onTrashPressed(recipient: String, domain: String) {
626696
val checkedIndexes = Pair(mutableListOf<Int>(), mutableListOf<Int>())
627697
model.devices.forEachIndexed { index, deviceItem ->
@@ -921,14 +991,16 @@ class SignInSceneController(
921991
override fun requestPermissionResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
922992
}
923993

924-
interface SignInUIObserver {
994+
interface SignInUIObserver: UIObserver {
925995
fun onSubmitButtonClicked()
926996
fun toggleUsernameFocusState(isFocused: Boolean)
927997
fun onSignUpLabelClicked()
928998
fun userLoginReady()
929999
fun onCantAccessDeviceClick()
1000+
fun onRecoveryCodeClicked()
9301001
fun onResendDeviceLinkAuth(username: String, domain: String)
9311002
fun onPasswordChangeListener(newPassword: String)
1003+
fun onRecoveryCodeChangeListener(newPassword: String)
9321004
fun onConfirmPasswordChangeListener(confirmPassword: String)
9331005
fun onUsernameTextChanged(newUsername: String)
9341006
fun onForgotPasswordClick()

src/main/kotlin/com/criptext/mail/scenes/signin/data/SignInDataSource.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ class SignInDataSource(override val runner: WorkRunner,
6464
flushResults(result)
6565
}
6666
)
67+
is SignInRequest.RecoveryCode -> RecoveryCodeWorker(
68+
httpClient = httpClient, jwt = params.tempToken,
69+
db = db,
70+
code = params.code,
71+
recipientId = params.recipientId,
72+
domain = params.domain,
73+
signUpDao = signUpDao,
74+
keyGenerator = keyGenerator,
75+
keyValueStorage = keyValueStorage,
76+
accountDao = accountDao,
77+
isMultiple = params.isMultiple,
78+
messagingInstance = MessagingInstance.Default(),
79+
publishFn = { result ->
80+
flushResults(result)
81+
}
82+
)
6783
is SignInRequest.LinkBegin -> LinkBeginWorker(
6884
httpClient = httpClient, username = params.username,
6985
domain = params.domain,

src/main/kotlin/com/criptext/mail/scenes/signin/data/SignInRequest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ sealed class SignInRequest{
1313

1414
data class ForgotPassword(val username: String, val domain: String): SignInRequest()
1515

16+
data class RecoveryCode(val recipientId: String, val domain: String, val tempToken: String, val isMultiple: Boolean, val code: String? = null): SignInRequest()
17+
1618
data class LinkBegin(val username: String, val domain: String): SignInRequest()
1719

1820
data class LinkAuth(val username: String, val ephemeralJwt: String, val domain: String, val password: String? = null): SignInRequest()

src/main/kotlin/com/criptext/mail/scenes/signin/data/SignInResult.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ sealed class SignInResult {
2828
val exception: Exception): ForgotPassword()
2929
}
3030

31+
sealed class RecoveryCode: SignInResult() {
32+
data class Success(val isValidate: Boolean): RecoveryCode()
33+
data class Failure(val isValidate: Boolean, val message: UIMessage,
34+
val exception: Exception): RecoveryCode()
35+
}
36+
3137
sealed class LinkBegin: SignInResult() {
3238
data class Success(val ephemeralJwt: String, val hasTwoFA: Boolean): LinkBegin()
3339
data class NoDevicesAvailable(val message: UIMessage): LinkBegin()

src/main/kotlin/com/criptext/mail/scenes/signin/holders/LoginValidationHolder.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class LoginValidationHolder(
2525
private var animLoading: AnimatorSet? = null
2626
private val rootLayout: View
2727
private val cantAccessDevice: TextView
28+
private val recoveryCodeText: TextView
2829
private val textViewTitle: TextView
2930
private val textViewBody: TextView
3031
private val textViewRejected: TextView
@@ -40,6 +41,7 @@ class LoginValidationHolder(
4041
init {
4142
rootLayout = view.findViewById<View>(R.id.viewRoot)
4243
cantAccessDevice = view.findViewById(R.id.cant_access_device)
44+
recoveryCodeText = view.findViewById(R.id.recovery_code)
4345
textViewTitle = view.findViewById(R.id.textViewTitle)
4446
textViewRejected = view.findViewById(R.id.device_rejected)
4547
textViewBody = view.findViewById(R.id.textViewBody)
@@ -53,6 +55,7 @@ class LoginValidationHolder(
5355
if(initialState.hasTwoFA){
5456
cantAccessDevice.visibility = View.GONE
5557
textViewTitle.text = view.context.getText(R.string.title_two_fa)
58+
recoveryCodeText.visibility = View.VISIBLE
5659
}
5760

5861
setListeners()
@@ -145,6 +148,10 @@ class LoginValidationHolder(
145148
uiObserver?.onCantAccessDeviceClick()
146149
}
147150

151+
recoveryCodeText.setOnClickListener {
152+
uiObserver?.onRecoveryCodeClicked()
153+
}
154+
148155
buttonResend.setOnClickListener {
149156
uiObserver?.onResendDeviceLinkAuth(initialState.username, initialState.domain)
150157
setEnableButtons(false)

0 commit comments

Comments
 (0)