Skip to content

Commit eb3b7ad

Browse files
committed
Address PR feedback, namely, moving more logic inside SyncRepository
1 parent db964b5 commit eb3b7ad

14 files changed

+260
-265
lines changed

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/SyncAccountRepository.kt

+38-11
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ import com.duckduckgo.sync.impl.CodeType.UNKNOWN
3737
import com.duckduckgo.sync.impl.ExchangeResult.*
3838
import com.duckduckgo.sync.impl.Result.Error
3939
import com.duckduckgo.sync.impl.Result.Success
40+
import com.duckduckgo.sync.impl.SyncAccountRepository.AuthCode
4041
import com.duckduckgo.sync.impl.pixels.*
42+
import com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeUrlWrapper
4143
import com.duckduckgo.sync.store.*
4244
import com.squareup.anvil.annotations.*
4345
import com.squareup.moshi.*
@@ -59,16 +61,28 @@ interface SyncAccountRepository {
5961
fun logout(deviceId: String): Result<Boolean>
6062
fun deleteAccount(): Result<Boolean>
6163
fun latestToken(): String
62-
fun getRecoveryCode(): Result<String>
64+
fun getRecoveryCode(): Result<AuthCode>
6365
fun getThisConnectedDevice(): ConnectedDevice?
6466
fun getConnectedDevices(): Result<List<ConnectedDevice>>
65-
fun getConnectQR(): Result<String>
67+
fun getConnectQR(): Result<AuthCode>
6668
fun pollConnectionKeys(): Result<Boolean>
67-
fun generateExchangeInvitationCode(): Result<String>
69+
fun generateExchangeInvitationCode(): Result<AuthCode>
6870
fun pollSecondDeviceExchangeAcknowledgement(): Result<Boolean>
6971
fun pollForRecoveryCodeAndLogin(): Result<ExchangeResult>
7072
fun renameDevice(device: ConnectedDevice): Result<Boolean>
7173
fun logoutAndJoinNewAccount(stringCode: String): Result<Boolean>
74+
75+
data class AuthCode(
76+
/**
77+
* A code that is suitable for displaying in a QR code.
78+
*/
79+
val qrCode: String,
80+
81+
/**
82+
* Just the code (b64-encoded)
83+
*/
84+
val rawCode: String,
85+
)
7286
}
7387

7488
@ContributesBinding(AppScope::class)
@@ -85,6 +99,7 @@ class AppSyncAccountRepository @Inject constructor(
8599
private val dispatcherProvider: DispatcherProvider,
86100
private val syncFeature: SyncFeature,
87101
private val deviceKeyGenerator: DeviceKeyGenerator,
102+
private val syncCodeUrlWrapper: SyncBarcodeUrlWrapper,
88103
) : SyncAccountRepository {
89104

90105
/**
@@ -300,13 +315,14 @@ class AppSyncAccountRepository @Inject constructor(
300315
)
301316
}
302317

303-
override fun getRecoveryCode(): Result<String> {
318+
override fun getRecoveryCode(): Result<AuthCode> {
304319
val primaryKey = syncStore.primaryKey ?: return Error(reason = "Get Recovery Code: Not existing primary Key").alsoFireAccountErrorPixel()
305320
val userID = syncStore.userId ?: return Error(reason = "Get Recovery Code: Not existing userId").alsoFireAccountErrorPixel()
306-
return Success(Adapters.recoveryCodeAdapter.toJson(LinkCode(RecoveryCode(primaryKey, userID))).encodeB64())
321+
val b64Encoded = Adapters.recoveryCodeAdapter.toJson(LinkCode(RecoveryCode(primaryKey, userID))).encodeB64()
322+
return Success(AuthCode(qrCode = b64Encoded, rawCode = b64Encoded))
307323
}
308324

309-
override fun generateExchangeInvitationCode(): Result<String> {
325+
override fun generateExchangeInvitationCode(): Result<AuthCode> {
310326
// Sync: InviteFlow - A (https://app.asana.com/0/72649045549333/1209571867429615)
311327
Timber.d("Sync-exchange: InviteFlow - A. Generating invitation code")
312328

@@ -321,14 +337,19 @@ class AppSyncAccountRepository @Inject constructor(
321337
val invitationWrapper = InvitationCodeWrapper(invitationCode)
322338

323339
return kotlin.runCatching {
324-
val code = Adapters.invitationCodeAdapter.toJson(invitationWrapper).encodeB64()
325-
Success(code)
340+
val b64Encoded = Adapters.invitationCodeAdapter.toJson(invitationWrapper).encodeB64()
341+
val qrCode = if (syncFeature.syncSetupBarcodeIsUrlBased().isEnabled()) {
342+
syncCodeUrlWrapper.decorateCode(b64Encoded)
343+
} else {
344+
b64Encoded
345+
}
346+
Success(AuthCode(qrCode = qrCode, rawCode = b64Encoded))
326347
}.getOrElse {
327348
Error(code = EXCHANGE_FAILED.code, reason = "Error generating invitation code").alsoFireAccountErrorPixel()
328349
}
329350
}
330351

331-
override fun getConnectQR(): Result<String> {
352+
override fun getConnectQR(): Result<AuthCode> {
332353
val prepareForConnect = kotlin.runCatching {
333354
nativeLib.prepareForConnect().also {
334355
it.checkResult("Creating ConnectQR code failed")
@@ -344,7 +365,13 @@ class AppSyncAccountRepository @Inject constructor(
344365
LinkCode(connect = ConnectCode(deviceId = deviceId, secretKey = prepareForConnect.publicKey)),
345366
) ?: return Error(reason = "Error generating Linking Code").alsoFireAccountErrorPixel()
346367

347-
return Success(linkingQRCode.encodeB64())
368+
val b64Encoded = linkingQRCode.encodeB64()
369+
val qrCode = if (syncFeature.syncSetupBarcodeIsUrlBased().isEnabled()) {
370+
syncCodeUrlWrapper.decorateCode(b64Encoded)
371+
} else {
372+
b64Encoded
373+
}
374+
return Success(AuthCode(qrCode = qrCode, rawCode = b64Encoded))
348375
}
349376

350377
private fun connectDevice(connectKeys: ConnectCode): Result<Boolean> {
@@ -465,7 +492,7 @@ class AppSyncAccountRepository @Inject constructor(
465492

466493
// recovery code comes b64 encoded, so we need to decode it, then encrypt, which automatically b64 encodes the encrypted form
467494
return kotlin.runCatching {
468-
val json = recoveryCode.data.decodeB64()
495+
val json = recoveryCode.data.rawCode.decodeB64()
469496
val encryptedJson = nativeLib.seal(json, publicKey)
470497
syncApi.sendEncryptedMessage(keyId, encryptedJson)
471498
}.getOrElse {

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncActivityViewModel.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,9 @@ class SyncActivityViewModel @Inject constructor(
310310

311311
fun generateRecoveryCode(viewContext: Context) {
312312
viewModelScope.launch(dispatchers.io()) {
313-
syncAccountRepository.getRecoveryCode().onSuccess { recoveryCodeB64 ->
313+
syncAccountRepository.getRecoveryCode().onSuccess { authCode ->
314314
kotlin.runCatching {
315-
recoveryCodePDF.generateAndStoreRecoveryCodePDF(viewContext, recoveryCodeB64)
315+
recoveryCodePDF.generateAndStoreRecoveryCodePDF(viewContext, authCode.rawCode)
316316
}.onSuccess { generateRecoveryCodePDF ->
317317
command.send(RecoveryCodePDFSuccess(generateRecoveryCodePDF))
318318
}.onFailure {

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncConnectViewModel.kt

+4-8
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.LoginSuccess
4848
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.ReadTextCode
4949
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.ShowError
5050
import com.duckduckgo.sync.impl.ui.SyncConnectViewModel.Command.ShowMessage
51-
import com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeDecorator
5251
import javax.inject.Inject
5352
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
5453
import kotlinx.coroutines.channels.Channel
@@ -68,7 +67,6 @@ class SyncConnectViewModel @Inject constructor(
6867
private val clipboard: Clipboard,
6968
private val syncPixels: SyncPixels,
7069
private val dispatchers: DispatcherProvider,
71-
private val urlDecorator: SyncBarcodeDecorator,
7270
) : ViewModel() {
7371
private val command = Channel<Command>(1, DROP_OLDEST)
7472
fun commands(): Flow<Command> = command.receiveAsFlow()
@@ -129,11 +127,9 @@ class SyncConnectViewModel @Inject constructor(
129127

130128
private suspend fun showQRCode() {
131129
syncAccountRepository.getConnectQR()
132-
.onSuccess { originalCode ->
130+
.onSuccess { code ->
133131
val qrBitmap = withContext(dispatchers.io()) {
134-
// wrap the code inside a URL if feature flag allows it
135-
val barcodeString = urlDecorator.decorateCode(originalCode, SyncBarcodeDecorator.CodeType.Connect)
136-
qrEncoder.encodeAsBitmap(barcodeString, dimen.qrSizeSmall, dimen.qrSizeSmall)
132+
qrEncoder.encodeAsBitmap(code.qrCode, dimen.qrSizeSmall, dimen.qrSizeSmall)
137133
}
138134
viewState.emit(viewState.value.copy(qrCodeBitmap = qrBitmap))
139135
}.onFailure {
@@ -150,8 +146,8 @@ class SyncConnectViewModel @Inject constructor(
150146
fun onCopyCodeClicked() {
151147
viewModelScope.launch(dispatchers.io()) {
152148
syncAccountRepository.getConnectQR().getOrNull()?.let { code ->
153-
Timber.d("Sync: recovery available for sharing manually: $code")
154-
clipboard.copyToClipboard(code)
149+
Timber.d("Sync: code available for sharing manually: $code")
150+
clipboard.copyToClipboard(code.rawCode)
155151
command.send(ShowMessage(R.string.sync_code_copied_message))
156152
} ?: command.send(FinishWithError)
157153
}

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncInternalSettingsViewModel.kt

+2-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import com.duckduckgo.sync.impl.ui.SyncInternalSettingsViewModel.Command.ReadCon
3333
import com.duckduckgo.sync.impl.ui.SyncInternalSettingsViewModel.Command.ReadQR
3434
import com.duckduckgo.sync.impl.ui.SyncInternalSettingsViewModel.Command.ShowMessage
3535
import com.duckduckgo.sync.impl.ui.SyncInternalSettingsViewModel.Command.ShowQR
36-
import com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeDecorator
3736
import com.duckduckgo.sync.store.*
3837
import javax.inject.Inject
3938
import kotlinx.coroutines.channels.BufferOverflow
@@ -55,7 +54,6 @@ constructor(
5554
private val syncEnvDataStore: SyncInternalEnvDataStore,
5655
private val syncFaviconFetchingStore: FaviconsFetchingStore,
5756
private val dispatchers: DispatcherProvider,
58-
private val urlDecorator: SyncBarcodeDecorator,
5957
) : ViewModel() {
6058

6159
private val command = Channel<Command>(1, BufferOverflow.DROP_OLDEST)
@@ -206,7 +204,7 @@ constructor(
206204
fun onShowQRClicked() {
207205
viewModelScope.launch(dispatchers.io()) {
208206
val recoveryCode = syncAccountRepository.getRecoveryCode().getOrNull() ?: return@launch
209-
command.send(ShowQR(recoveryCode))
207+
command.send(ShowQR(recoveryCode.rawCode))
210208
}
211209
}
212210

@@ -245,7 +243,7 @@ constructor(
245243
}
246244

247245
is Success -> {
248-
urlDecorator.decorateCode(qrCodeResult.data, SyncBarcodeDecorator.CodeType.Connect)
246+
qrCodeResult.data.qrCode
249247
}
250248
}
251249
updateViewState()

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncWithAnotherActivityViewModel.kt

+9-28
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import com.duckduckgo.sync.impl.R.string
4040
import com.duckduckgo.sync.impl.Result.Error
4141
import com.duckduckgo.sync.impl.Result.Success
4242
import com.duckduckgo.sync.impl.SyncAccountRepository
43+
import com.duckduckgo.sync.impl.SyncAccountRepository.AuthCode
4344
import com.duckduckgo.sync.impl.SyncFeature
4445
import com.duckduckgo.sync.impl.onFailure
4546
import com.duckduckgo.sync.impl.onSuccess
@@ -52,9 +53,6 @@ import com.duckduckgo.sync.impl.ui.SyncWithAnotherActivityViewModel.Command.Read
5253
import com.duckduckgo.sync.impl.ui.SyncWithAnotherActivityViewModel.Command.ShowError
5354
import com.duckduckgo.sync.impl.ui.SyncWithAnotherActivityViewModel.Command.ShowMessage
5455
import com.duckduckgo.sync.impl.ui.SyncWithAnotherActivityViewModel.Command.SwitchAccountSuccess
55-
import com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeDecorator
56-
import com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeDecorator.CodeType.Exchange
57-
import com.duckduckgo.sync.impl.ui.qrcode.SyncBarcodeDecorator.CodeType.Recovery
5856
import com.duckduckgo.sync.impl.ui.setup.EnterCodeContract.EnterCodeContractOutput
5957
import javax.inject.Inject
6058
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
@@ -76,12 +74,11 @@ class SyncWithAnotherActivityViewModel @Inject constructor(
7674
private val syncPixels: SyncPixels,
7775
private val dispatchers: DispatcherProvider,
7876
private val syncFeature: SyncFeature,
79-
private val urlDecorator: SyncBarcodeDecorator,
8077
) : ViewModel() {
8178
private val command = Channel<Command>(1, DROP_OLDEST)
8279
fun commands(): Flow<Command> = command.receiveAsFlow()
8380

84-
private var barcodeContents: BarcodeContents? = null
81+
private var barcodeContents: AuthCode? = null
8582

8683
private val viewState = MutableStateFlow(ViewState())
8784
fun viewState(): Flow<ViewState> = viewState.onStart {
@@ -113,20 +110,17 @@ class SyncWithAnotherActivityViewModel @Inject constructor(
113110

114111
private suspend fun showQRCode() {
115112
// get the code as a Result, and pair it with the type of code we're dealing with
116-
val (result, codeType) = if (!syncFeature.exchangeKeysToSyncWithAnotherDevice().isEnabled()) {
117-
Pair(syncAccountRepository.getRecoveryCode(), Recovery)
113+
val result = if (!syncFeature.exchangeKeysToSyncWithAnotherDevice().isEnabled()) {
114+
syncAccountRepository.getRecoveryCode()
118115
} else {
119-
Pair(syncAccountRepository.generateExchangeInvitationCode(), Exchange)
116+
syncAccountRepository.generateExchangeInvitationCode()
120117
}
121118

122-
result.onSuccess { code ->
123-
// wrap the code inside a URL if feature flag allows it
124-
val barcodeString = urlDecorator.decorateCode(code, codeType)
125-
126-
barcodeContents = BarcodeContents(underlyingCode = code, barcodeString = barcodeString)
119+
result.onSuccess { authCode ->
120+
barcodeContents = authCode
127121

128122
val qrBitmap = withContext(dispatchers.io()) {
129-
qrEncoder.encodeAsBitmap(barcodeString, dimen.qrSizeSmall, dimen.qrSizeSmall)
123+
qrEncoder.encodeAsBitmap(authCode.qrCode, dimen.qrSizeSmall, dimen.qrSizeSmall)
130124
}
131125

132126
viewState.emit(viewState.value.copy(qrCodeBitmap = qrBitmap))
@@ -144,7 +138,7 @@ class SyncWithAnotherActivityViewModel @Inject constructor(
144138
fun onCopyCodeClicked() {
145139
viewModelScope.launch(dispatchers.io()) {
146140
barcodeContents?.let { contents ->
147-
clipboard.copyToClipboard(contents.underlyingCode)
141+
clipboard.copyToClipboard(contents.rawCode)
148142
command.send(ShowMessage(string.sync_code_copied_message))
149143
} ?: command.send(FinishWithError)
150144
}
@@ -309,17 +303,4 @@ class SyncWithAnotherActivityViewModel @Inject constructor(
309303
fun onUserAskedToSwitchAccount() {
310304
syncPixels.fireAskUserToSwitchAccount()
311305
}
312-
313-
private data class BarcodeContents(
314-
/**
315-
* The underlying code that was encoded in the barcode.
316-
* It's possible this is different from the barcode string which might contain extra data
317-
*/
318-
val underlyingCode: String,
319-
320-
/**
321-
* The string that was encoded in the barcode.
322-
*/
323-
val barcodeString: String,
324-
)
325306
}

sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/qrcode/SyncBarcodeDecorator.kt

-97
This file was deleted.

0 commit comments

Comments
 (0)