Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
53f2564
chore(multisig): Miscellaneous updates and fixes
yasin-ce Jan 20, 2026
cd2fcb7
feat(multisig): Implement joint account creation flow
yasin-ce Jan 20, 2026
ec9a896
feat(multisig): Implement inbox pages for sign requests
yasin-ce Jan 20, 2026
2954595
fix: Resolve merge conflicts and update domain model naming
yasin-ce Jan 22, 2026
7466119
fix: Update app inbox mappers to use correct domain model names
yasin-ce Jan 22, 2026
389afda
Merge branch 'multisig/07-creation-flow' into multisig/08-inbox-pages
yasin-ce Jan 23, 2026
2d2d8b0
Fix JointAccountInvitationDetailFragment to use InboxApiRepository
yasin-ce Jan 23, 2026
816af45
Merge branch 'multisig/07-creation-flow' into multisig/08-inbox-pages
yasin-ce Jan 23, 2026
d8a49ec
Merge branch 'multisig/07-creation-flow' into multisig/08-inbox-pages
yasin-ce Jan 26, 2026
db2437a
Merge branch 'multisig/07-creation-flow' into multisig/08-inbox-pages
yasin-ce Jan 26, 2026
4592163
fix: Address PR #518 review comments
yasin-ce Jan 26, 2026
d0a7bfa
refactor: Use TimeProvider and RelativeTimeDifference in SignatureReq…
yasin-ce Jan 26, 2026
ed0641d
refactor: Move deep link handling flag to ViewModel and implement Inb…
yasin-ce Jan 26, 2026
0e0a8b0
refactor: Move account display logic to JointAccountInvitationDetailV…
yasin-ce Jan 26, 2026
d8d0d97
refactor: Simplify InboxScreenPreview by using StateFlow overload
yasin-ce Jan 26, 2026
03876f2
refactor: Fix InboxLastOpenedTimeLocalSource DI with PersistentCacheP…
yasin-ce Jan 26, 2026
0d6583f
refactor: Implement ViewState/ViewEvent pattern for Inbox
yasin-ce Jan 26, 2026
13d3ab6
fix: Address PR review feedback and add coding rules
yasin-ce Jan 26, 2026
31bfdc0
fix: Address PR review feedback and add coding rules
yasin-ce Jan 26, 2026
5603799
Merge branch 'multisig/08-inbox-pages' of github.com:perawallet/pera-…
yasin-ce Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ render.experimental.xml

# Claude
.claude/

# Backend Feedback
BACKEND_FEEDBACK.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ internal class GetAccountOriginalStateIconDrawablePreviewUseCase @Inject constru
R.color.layer_gray_lighter
}
}

// TODO: Handle Joint Account properly
AccountType.Joint -> STANDARD.backgroundColorResId
}
}

Expand All @@ -86,9 +83,6 @@ internal class GetAccountOriginalStateIconDrawablePreviewUseCase @Inject constru
AccountType.RekeyedAuth, AccountType.Rekeyed, null -> {
if (accountType?.canSignTransaction() == true) STANDARD.iconResId else R.drawable.ic_question
}

// TODO: Handle Joint Account properly
AccountType.Joint -> STANDARD.iconResId
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@ import com.algorand.android.modules.accounts.lite.domain.model.AccountLiteCacheS
import com.algorand.android.modules.accounts.lite.domain.model.AccountLiteCacheStatus.Loading
import com.algorand.android.modules.accounts.lite.domain.usecase.GetAccountLiteCacheFlow
import com.algorand.android.modules.accounts.ui.model.AccountPreview
import com.algorand.android.modules.inbox.allaccounts.ui.usecase.InboxPreviewUseCase
import com.algorand.android.modules.parity.domain.model.SelectedCurrencyDetail
import com.algorand.android.modules.peraconnectivitymanager.ui.PeraConnectivityManager
import com.algorand.android.utils.CacheResult
import com.algorand.wallet.banner.domain.usecase.GetBannerFlow
import com.algorand.wallet.inbox.asset.domain.usecase.GetAssetInboxRequestCountFlow
import com.algorand.wallet.privacy.domain.usecase.GetPrivacyModeFlow
import com.algorand.wallet.remoteconfig.domain.model.FeatureToggle
import com.algorand.wallet.remoteconfig.domain.usecase.IsFeatureToggleEnabled
import com.algorand.wallet.spotbanner.domain.model.SpotBannerFlowData
import com.algorand.wallet.spotbanner.domain.usecase.GetSpotBannersFlow
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -42,10 +46,13 @@ class AccountsPreviewUseCase @Inject constructor(
private val portfolioValueItemMapper: PortfolioValueItemMapper,
private val peraConnectivityManager: PeraConnectivityManager,
private val accountPreviewProcessor: AccountPreviewProcessor,
private val getAssetInboxRequestCountFlow: GetAssetInboxRequestCountFlow,
private val getAccountLiteCacheFlow: GetAccountLiteCacheFlow,
private val getPrivacyModeFlow: GetPrivacyModeFlow,
private val getBannerFlow: GetBannerFlow,
private val getSpotBannersFlow: GetSpotBannersFlow,
private val inboxPreviewUseCase: InboxPreviewUseCase,
private val isFeatureToggleEnabled: IsFeatureToggleEnabled
) {

suspend fun getInitialAccountPreview(): AccountPreview {
Expand Down Expand Up @@ -81,19 +88,40 @@ class AccountsPreviewUseCase @Inject constructor(
return combine(
getBannerFlow(),
getSpotBannersFlow(getSpotBannerFlowData(accountLiteCacheData)),
getTotalInboxCountFlow(),
getPrivacyModeFlow()
) { banner, spotBanners, privacyMode ->
) { banner, spotBanners, totalInboxCount, privacyMode ->
accountPreviewProcessor.prepareAccountPreview(
accountLiteCacheData.localAccounts,
accountLiteCacheData.accountLites,
banner,
0,
totalInboxCount,
privacyMode,
spotBanners
)
}
}

@OptIn(
kotlinx.coroutines.FlowPreview::class,
kotlinx.coroutines.ExperimentalCoroutinesApi::class
)
private suspend fun getTotalInboxCountFlow(): Flow<Int> {
val isJointAccountEnabled = isFeatureToggleEnabled(FeatureToggle.JOINT_ACCOUNT.key)
return getAssetInboxRequestCountFlow().flatMapLatest { asaInboxCount ->
inboxPreviewUseCase.getInboxPreview()
.mapLatest { inboxPreview ->
if (isJointAccountEnabled) {
val jointAccountSignRequestsCount = inboxPreview.signatureRequestList.size
val jointAccountImportRequestsCount = inboxPreview.jointAccountInvitationList.size
asaInboxCount + jointAccountSignRequestsCount + jointAccountImportRequestsCount
} else {
asaInboxCount
}
}
}
}

private fun getSpotBannerFlowData(accountLiteCacheData: Data): List<SpotBannerFlowData> {
return accountLiteCacheData.accountLites.values.map { lite ->
with(lite) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ class Arc59ReceiveDetailFragment : BaseFragment(R.layout.fragment_arc59_receive_
initObservers()
viewModel.initializePreview()
arc59ClaimRejectTransactionSignManager.setup(viewLifecycleOwner.lifecycle)
initSavedStateListener()
}

override fun onResume() {
super.onResume()
private fun initSavedStateListener() {
startSavedStateListener(R.id.arc59ReceiveDetailFragment) {
useSavedStateValue<ConfirmationBottomSheetResult>(RESULT_KEY) {
if (it.isAccepted) viewModel.rejectTransaction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.google.android.material.button.MaterialButton
class AssetInboxInfoBottomSheet : BaseInformationBottomSheet() {

override fun initTitleTextView(titleTextView: TextView) {
titleTextView.setText(R.string.asset_transfer_requests)
titleTextView.setText(R.string.inbox)
}

override fun initDescriptionTextView(descriptionTextView: TextView) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.inbox.allaccounts.di

import com.algorand.android.modules.inbox.allaccounts.ui.mapper.InboxPreviewMapper
import com.algorand.android.modules.inbox.allaccounts.ui.mapper.InboxPreviewMapperImpl
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object InboxRepositoryModule {

@Provides
@Singleton
fun provideInboxPreviewMapper(
inboxPreviewMapperImpl: InboxPreviewMapperImpl
): InboxPreviewMapper = inboxPreviewMapperImpl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.inbox.allaccounts.domain.model

import android.os.Parcelable
import com.algorand.android.models.RecyclerListItem
import com.algorand.android.modules.accountcore.ui.model.AccountDisplayName
import com.algorand.android.modules.accounticon.ui.model.AccountIconDrawablePreview
import kotlinx.parcelize.Parcelize

@Parcelize
data class InboxWithAccount(
val address: String,
val requestCount: Int,
val accountDisplayName: AccountDisplayName,
val accountAddress: String,
val accountIconDrawablePreview: AccountIconDrawablePreview
) : Parcelable, RecyclerListItem {
override fun areItemsTheSame(other: RecyclerListItem): Boolean {
return other is InboxWithAccount && accountAddress == other.accountAddress
}

override fun areContentsTheSame(other: RecyclerListItem): Boolean {
return other is InboxWithAccount && this == other
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2022-2025 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.android.modules.inbox.allaccounts.domain.model

import android.os.Parcelable
import com.algorand.android.models.RecyclerListItem
import com.algorand.android.modules.accounticon.ui.model.AccountIconDrawablePreview
import kotlinx.parcelize.Parcelize

@Parcelize
data class SignatureRequestInboxItem(
val signRequestId: String,
val jointAccountAddress: String,
val jointAccountAddressShortened: String,
val accountIconDrawablePreview: AccountIconDrawablePreview,
val description: String,
val timeAgo: String,
val signedCount: Int,
val totalCount: Int,
val timeLeft: String?,
val isRead: Boolean = true,
val isExpired: Boolean = false,
val canUserSign: Boolean = true
) : Parcelable, RecyclerListItem {
override fun areItemsTheSame(other: RecyclerListItem): Boolean {
return other is SignatureRequestInboxItem && signRequestId == other.signRequestId
}

override fun areContentsTheSame(other: RecyclerListItem): Boolean {
return other is SignatureRequestInboxItem && this == other
}
}
Loading
Loading