diff --git a/wallet/res/layout/fragment_voting_request_details.xml b/wallet/res/layout/fragment_voting_request_details.xml
index c73f50324..5fcf116b9 100644
--- a/wallet/res/layout/fragment_voting_request_details.xml
+++ b/wallet/res/layout/fragment_voting_request_details.xml
@@ -48,75 +48,6 @@
android:layout_marginTop="5dp"
android:text="@string/after_the_voting" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ tools:text="JohnDoe" />
+ tools:text="GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" />
-
-
-
+
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/wallet/res/values/strings-dashpay.xml b/wallet/res/values/strings-dashpay.xml
index 04a023e82..33de39f14 100644
--- a/wallet/res/values/strings-dashpay.xml
+++ b/wallet/res/values/strings-dashpay.xml
@@ -308,6 +308,7 @@
Review the posting bellow to verify the ownership of this username
Vote to Approve
Identity
+ Results
Link
Not provided
Cancel Approval
diff --git a/wallet/src/de/schildbach/wallet/database/BlockchainStateRoomConverters.kt b/wallet/src/de/schildbach/wallet/database/BlockchainStateRoomConverters.kt
index 23d8bf046..b6639fa17 100644
--- a/wallet/src/de/schildbach/wallet/database/BlockchainStateRoomConverters.kt
+++ b/wallet/src/de/schildbach/wallet/database/BlockchainStateRoomConverters.kt
@@ -26,6 +26,8 @@ import org.dashj.platform.dpp.identity.Identity
import org.dash.wallet.common.data.entity.BlockchainState
import org.bitcoinj.core.Coin
import org.bitcoinj.core.Sha256Hash
+import org.dashj.platform.dashpay.IdentityStatus
+import org.dashj.platform.dashpay.UsernameStatus
import org.dashj.platform.sdk.KeyType
import java.util.*
import kotlin.collections.ArrayList
@@ -78,22 +80,22 @@ class BlockchainStateRoomConverters {
}
@TypeConverter
- fun toUsernameStatus(value: Int): BlockchainIdentity.UsernameStatus {
- return BlockchainIdentity.UsernameStatus.values()[value]
+ fun toUsernameStatus(value: Int): UsernameStatus {
+ return UsernameStatus.values()[value]
}
@TypeConverter
- fun fromUsernameStatus(usernameStatus: BlockchainIdentity.UsernameStatus?): Int {
- return usernameStatus?.value ?: BlockchainIdentity.UsernameStatus.NOT_PRESENT.value
+ fun fromUsernameStatus(usernameStatus: UsernameStatus?): Int {
+ return usernameStatus?.value ?: UsernameStatus.NOT_PRESENT.value
}
@TypeConverter
- fun toRegistrationStatus(value: Int): BlockchainIdentity.RegistrationStatus? {
- return if (value > -1) BlockchainIdentity.RegistrationStatus.values()[value] else null
+ fun toRegistrationStatus(value: Int): IdentityStatus? {
+ return if (value > -1) IdentityStatus.values()[value] else null
}
@TypeConverter
- fun fromRegistrationStatus(registrationStatus: BlockchainIdentity.RegistrationStatus?): Int {
+ fun fromRegistrationStatus(registrationStatus: IdentityStatus?): Int {
return registrationStatus?.ordinal ?: -1
}
diff --git a/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityBaseData.kt b/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityBaseData.kt
index 5319d7d33..f414cb7de 100644
--- a/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityBaseData.kt
+++ b/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityBaseData.kt
@@ -19,6 +19,7 @@ package de.schildbach.wallet.database.entity
import de.schildbach.wallet.data.InvitationLinkData
import org.bitcoinj.core.Sha256Hash
+import org.dashj.platform.dashpay.UsernameRequestStatus
data class BlockchainIdentityBaseData(
val id: Int,
@@ -33,14 +34,14 @@ data class BlockchainIdentityBaseData(
var requestedUsername: String? = null,
var verificationLink: String? = null,
val cancelledVerificationLink: Boolean? = null,
- val usernameRequested: Boolean? = null,
+ val usernameRequested: UsernameRequestStatus? = null,
val votingPeriodStart: Long? = null
) {
val creationInProgress: Boolean
get() = creationState > BlockchainIdentityData.CreationState.NONE &&
creationState < BlockchainIdentityData.CreationState.VOTING &&
- creationStateErrorMessage == null
+ creationStateErrorMessage == null && !restoring
val votingInProgress: Boolean
get() = creationState == BlockchainIdentityData.CreationState.VOTING
diff --git a/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityData.kt b/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityData.kt
index 8e9b907e9..2b95dc621 100644
--- a/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityData.kt
+++ b/wallet/src/de/schildbach/wallet/database/entity/BlockchainIdentityData.kt
@@ -36,7 +36,9 @@ import org.bitcoinj.wallet.Wallet
import org.bitcoinj.wallet.authentication.AuthenticationGroupExtension
import org.dash.wallet.common.WalletDataProvider
import org.dash.wallet.common.data.BaseConfig
-import org.dashj.platform.dashpay.BlockchainIdentity
+import org.dashj.platform.dashpay.IdentityStatus
+import org.dashj.platform.dashpay.UsernameRequestStatus
+import org.dashj.platform.dashpay.UsernameStatus
import org.dashj.platform.dpp.identity.Identity
import org.dashj.platform.dpp.toHex
import org.dashj.platform.dpp.util.Converters
@@ -55,8 +57,8 @@ data class BlockchainIdentityData(
var usingInvite: Boolean = false,
var invite: InvitationLinkData? = null,
var preorderSalt: ByteArray? = null,
- var registrationStatus: BlockchainIdentity.RegistrationStatus? = null,
- var usernameStatus: BlockchainIdentity.UsernameStatus? = null,
+ var registrationStatus: IdentityStatus? = null,
+ var usernameStatus: UsernameStatus? = null,
var privacyMode: CoinJoinMode? = null,
var creditBalance: Coin? = null,
var activeKeyCount: Int? = null,
@@ -64,11 +66,10 @@ data class BlockchainIdentityData(
var keysCreated: Long? = null,
var currentMainKeyIndex: Int? = null,
var currentMainKeyType: KeyType? = null,
- var requestedUsername: String? = null,
var verificationLink: String? = null,
val cancelledVerificationLink: Boolean? = null,
- val usernameRequested: Boolean? = null,
- val votingPeriodStart: Long? = null
+ var usernameRequested: UsernameRequestStatus? = null,
+ var votingPeriodStart: Long? = null
) {
var id = 1
@@ -161,10 +162,10 @@ open class BlockchainIdentityConfig @Inject constructor(
val PRIVACY_MODE = stringPreferencesKey("privacy_mode")
val BALANCE = longPreferencesKey("identity_balance")
- val REQUESTED_USERNAME = stringPreferencesKey("requested_username")
+ // val REQUESTED_USERNAME = stringPreferencesKey("requested_username")
val REQUESTED_USERNAME_LINK = stringPreferencesKey("requested_username_link")
val CANCELED_REQUESTED_USERNAME_LINK = booleanPreferencesKey("cancelled_requested_username_link")
- val USERNAME_REQUESTED = booleanPreferencesKey("username_requested")
+ val USERNAME_REQUESTED = stringPreferencesKey("username_requested")
val VOTING_PERIOD_START = longPreferencesKey("voting_period_start")
}
@@ -182,14 +183,13 @@ open class BlockchainIdentityConfig @Inject constructor(
usingInvite = prefs[USING_INVITE] ?: false,
invite = prefs[INVITE_LINK]?.let { InvitationLinkData(Uri.parse(it), false) },
preorderSalt = prefs[PREORDER_SALT]?.let { Converters.fromHex(it) },
- registrationStatus = prefs[IDENTITY_REGISTRATION_STATUS]?.let { BlockchainIdentity.RegistrationStatus.valueOf(it) },
- usernameStatus = prefs[USERNAME_REGISTRATION_STATUS]?.let { BlockchainIdentity.UsernameStatus.valueOf(it) },
+ registrationStatus = prefs[IDENTITY_REGISTRATION_STATUS]?.let { IdentityStatus.valueOf(it) },
+ usernameStatus = prefs[USERNAME_REGISTRATION_STATUS]?.let { UsernameStatus.valueOf(it) },
privacyMode = prefs[PRIVACY_MODE]?.let { CoinJoinMode.valueOf(it) },
creditBalance = prefs[BALANCE]?.let { Coin.valueOf(it) },
- requestedUsername = prefs[REQUESTED_USERNAME],
verificationLink = prefs[REQUESTED_USERNAME_LINK],
cancelledVerificationLink = prefs[CANCELED_REQUESTED_USERNAME_LINK],
- usernameRequested = prefs[USERNAME_REQUESTED],
+ usernameRequested = prefs[USERNAME_REQUESTED]?.let { UsernameRequestStatus.valueOf(it) },
votingPeriodStart = prefs[VOTING_PERIOD_START]
)
}
@@ -206,10 +206,9 @@ open class BlockchainIdentityConfig @Inject constructor(
creditFundingTxId = prefs[ASSET_LOCK_TXID]?.let { Sha256Hash.wrap(it) },
usingInvite = prefs[USING_INVITE] ?: false,
invite = prefs[INVITE_LINK]?.let { InvitationLinkData(Uri.parse(it), false) },
- requestedUsername = prefs[REQUESTED_USERNAME],
verificationLink = prefs[REQUESTED_USERNAME_LINK],
cancelledVerificationLink = prefs[CANCELED_REQUESTED_USERNAME_LINK],
- usernameRequested = prefs[USERNAME_REQUESTED],
+ usernameRequested = prefs[USERNAME_REQUESTED]?.let { UsernameRequestStatus.valueOf(it) },
votingPeriodStart = prefs[VOTING_PERIOD_START]
)
}
diff --git a/wallet/src/de/schildbach/wallet/database/entity/UsernameRequest.kt b/wallet/src/de/schildbach/wallet/database/entity/UsernameRequest.kt
index 5daf2d9d3..9b68dfa83 100644
--- a/wallet/src/de/schildbach/wallet/database/entity/UsernameRequest.kt
+++ b/wallet/src/de/schildbach/wallet/database/entity/UsernameRequest.kt
@@ -25,9 +25,17 @@ data class UsernameRequest(
@PrimaryKey
val requestId: String,
val username: String,
+ val normalizedLabel: String,
val createdAt: Long,
val identity: String,
- val link: String?,
+ var link: String?,
val votes: Int,
+ val lockVotes: Int,
val isApproved: Boolean
-)
+) {
+ companion object {
+ fun getRequestId(identity: String, username: String): String {
+ return String.format("%s %s", identity, username)
+ }
+ }
+}
diff --git a/wallet/src/de/schildbach/wallet/service/platform/PlatformBroadcastService.kt b/wallet/src/de/schildbach/wallet/service/platform/PlatformBroadcastService.kt
index 4a51a2bd4..489f5c63e 100644
--- a/wallet/src/de/schildbach/wallet/service/platform/PlatformBroadcastService.kt
+++ b/wallet/src/de/schildbach/wallet/service/platform/PlatformBroadcastService.kt
@@ -28,6 +28,8 @@ import org.dash.wallet.common.WalletDataProvider
import org.dash.wallet.common.services.analytics.AnalyticsConstants
import org.dash.wallet.common.services.analytics.AnalyticsService
import org.dash.wallet.common.services.analytics.AnalyticsTimer
+import org.dashj.platform.dashpay.callback.WalletSignerCallback
+import org.dashj.platform.wallet.IdentityVerifyDocument
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import javax.inject.Inject
@@ -36,6 +38,7 @@ interface PlatformBroadcastService {
suspend fun broadcastUpdatedProfile(dashPayProfile: DashPayProfile, encryptionKey: KeyParameter): DashPayProfile
suspend fun sendContactRequest(toUserId: String): DashPayContactRequest
suspend fun sendContactRequest(toUserId: String, encryptionKey: KeyParameter): DashPayContactRequest
+ suspend fun broadcastIdentityVerify(username: String, url: String, encryptionKey: KeyParameter?): IdentityVerifyDocument
}
class PlatformDocumentBroadcastService @Inject constructor(
@@ -93,6 +96,25 @@ class PlatformDocumentBroadcastService @Inject constructor(
return dashPayContactRequest
}
+ override suspend fun broadcastIdentityVerify(username: String, url: String, encryptionKey: KeyParameter?): IdentityVerifyDocument {
+ val blockchainIdentity = platformRepo.blockchainIdentity
+
+ // Create Identity Verify
+ val timer = AnalyticsTimer(analytics, log, AnalyticsConstants.Process.PROCESS_CONTACT_REQUEST_SEND)
+ val identityVerifyDocument = platform.identityVerify.createForDashDomain(
+ username,
+ url,
+ blockchainIdentity.identity!!,
+ WalletSignerCallback(walletDataProvider.wallet!!, encryptionKey)
+ )
+ timer.logTiming()
+ log.info("identity verify sent")
+
+ log.info("contact request: $identityVerifyDocument")
+
+ return identityVerifyDocument
+ }
+
@Throws(Exception::class)
override suspend fun broadcastUpdatedProfile(dashPayProfile: DashPayProfile, encryptionKey: KeyParameter): DashPayProfile {
log.info("broadcast profile")
diff --git a/wallet/src/de/schildbach/wallet/service/platform/PlatformService.kt b/wallet/src/de/schildbach/wallet/service/platform/PlatformService.kt
index 6be91db87..f39f8d0bd 100644
--- a/wallet/src/de/schildbach/wallet/service/platform/PlatformService.kt
+++ b/wallet/src/de/schildbach/wallet/service/platform/PlatformService.kt
@@ -37,6 +37,7 @@ import org.dashj.platform.sdk.platform.Identities
import org.dashj.platform.sdk.platform.Names
import org.dashj.platform.sdk.platform.Platform
import org.dashj.platform.sdk.platform.PlatformStateRepository
+import org.dashj.platform.wallet.IdentityVerify
import org.slf4j.LoggerFactory
import javax.inject.Inject
@@ -52,6 +53,7 @@ interface PlatformService {
val names: Names
val profiles: Profiles
val contactRequests: ContactRequests
+ val identityVerify: IdentityVerify
val client: DapiClient
val params: NetworkParameters
@@ -66,6 +68,7 @@ class PlatformServiceImplementation @Inject constructor(
override val platform = Platform(Constants.NETWORK_PARAMETERS)
override val profiles = Profiles(platform)
override val contactRequests = ContactRequests(platform)
+ override val identityVerify = IdentityVerify(platform)
override val dpp: DashPlatformProtocol = platform.dpp
override val stateRepository: PlatformStateRepository = platform.stateRepository
override val identities: Identities = platform.identities
diff --git a/wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt b/wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt
index 371688a7b..6a5334f93 100644
--- a/wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt
+++ b/wallet/src/de/schildbach/wallet/service/platform/PlatformSyncService.kt
@@ -33,12 +33,14 @@ import de.schildbach.wallet.database.dao.DashPayProfileDao
import de.schildbach.wallet.database.dao.InvitationsDao
import de.schildbach.wallet.database.dao.TransactionMetadataChangeCacheDao
import de.schildbach.wallet.database.dao.TransactionMetadataDocumentDao
+import de.schildbach.wallet.database.dao.UsernameRequestDao
import de.schildbach.wallet.database.entity.BlockchainIdentityConfig
import de.schildbach.wallet.database.entity.BlockchainIdentityData
import de.schildbach.wallet.database.entity.DashPayContactRequest
import de.schildbach.wallet.database.entity.DashPayProfile
import de.schildbach.wallet.database.entity.TransactionMetadataCacheItem
import de.schildbach.wallet.database.entity.TransactionMetadataDocument
+import de.schildbach.wallet.database.entity.UsernameRequest
import de.schildbach.wallet.livedata.SeriousError
import de.schildbach.wallet.security.SecurityGuard
import de.schildbach.wallet.service.BlockchainService
@@ -70,6 +72,7 @@ import org.dashj.platform.contracts.wallet.TxMetadataItem
import org.dashj.platform.dashpay.ContactRequest
import org.dashj.platform.dpp.identifier.Identifier
import org.dashj.platform.sdk.platform.DomainDocument
+import org.dashj.platform.wallet.IdentityVerify
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.HashMap
@@ -94,6 +97,7 @@ interface PlatformSyncService {
suspend fun updateContactRequests()
fun postUpdateBloomFilters()
+ suspend fun updateUsernameRequestsWithVotes()
fun addContactsUpdatedListener(listener: OnContactsUpdated)
fun removeContactsUpdatedListener(listener: OnContactsUpdated?)
@@ -119,7 +123,8 @@ class PlatformSynchronizationService @Inject constructor(
private val blockchainIdentityDataDao: BlockchainIdentityConfig,
private val dashPayProfileDao: DashPayProfileDao,
private val dashPayContactRequestDao: DashPayContactRequestDao,
- private val invitationsDao: InvitationsDao
+ private val invitationsDao: InvitationsDao,
+ private val usernameRequestDao: UsernameRequestDao
) : PlatformSyncService {
companion object {
@@ -212,17 +217,17 @@ class PlatformSynchronizationService @Inject constructor(
return
}
- val blockchainIdentityData = blockchainIdentityDataDao.load() ?: return
- if (blockchainIdentityData.creationState < BlockchainIdentityData.CreationState.DONE) {
- log.info("update contacts not completed username registration/recovery is not complete")
- return
- }
+ try {
+ val blockchainIdentityData = blockchainIdentityDataDao.load() ?: return
+ if (blockchainIdentityData.creationState < BlockchainIdentityData.CreationState.DONE) {
+ log.info("update contacts not completed username registration/recovery is not complete")
+ return
+ }
- if (blockchainIdentityData.username == null || blockchainIdentityData.userId == null) {
- return // this is here because the wallet is being reset without removing blockchainIdentityData
- }
+ if (blockchainIdentityData.username == null || blockchainIdentityData.userId == null) {
+ return // this is here because the wallet is being reset without removing blockchainIdentityData
+ }
- try {
val userId = blockchainIdentityData.userId!!
val userIdList = HashSet()
@@ -324,7 +329,7 @@ class PlatformSynchronizationService @Inject constructor(
// fetch updated invitations
async { updateInvitations() },
// fetch updated transaction metadata
- async { updateTransactionMetadata() },
+ async { updateTransactionMetadata() }, // TODO: this is skipped in VOTING state, but shouldn't be
// fetch updated profiles from the network
async { updateContactProfiles(userId, lastContactRequestTime) }
)
@@ -1019,12 +1024,40 @@ class PlatformSynchronizationService @Inject constructor(
log.info("publishing updates to tx metadata items complete")
}
+ override suspend fun updateUsernameRequestsWithVotes() {
+ val contestedNames = platform.platform.names.getContestedNames()
+ for (name in contestedNames) {
+ val voteContender = platform.platform.names.getVoteContenders(name)
+ voteContender.map.forEach { (identifier, contender) ->
+
+ val contestedDocument = DomainDocument(
+ platform.platform.names.deserialize(contender.seralizedDocument)
+ )
+
+ val identityVerifyDocument = IdentityVerify(platform.platform).get(identifier, name)
+
+ val usernameRequest = UsernameRequest(
+ UsernameRequest.getRequestId(identifier.toString(), name),
+ contestedDocument.label,
+ name,
+ contestedDocument.createdAt?.div(1000) ?: -1L,
+ identifier.toString(),
+ identityVerifyDocument?.url,
+ contender.votes,
+ voteContender.lockVoteTally,
+ false
+ )
+ usernameRequestDao.insert(usernameRequest)
+ }
+ }
+ }
+
override suspend fun triggerPreBlockDownloadComplete() {
finishPreBlockDownload()
}
private suspend fun finishPreBlockDownload() {
- log.info("PreDownloadBlocks: complete")
+ log.info("PreBlockDownload: complete")
if (config.areNotificationsDisabled()) {
// this will enable notifications, since platform information has been synced
config.set(DashPayConfig.LAST_SEEN_NOTIFICATION_TIME, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7))
diff --git a/wallet/src/de/schildbach/wallet/ui/dashpay/CreateIdentityService.kt b/wallet/src/de/schildbach/wallet/ui/dashpay/CreateIdentityService.kt
index 72ea4b75c..edd7cfe55 100644
--- a/wallet/src/de/schildbach/wallet/ui/dashpay/CreateIdentityService.kt
+++ b/wallet/src/de/schildbach/wallet/ui/dashpay/CreateIdentityService.kt
@@ -11,10 +11,12 @@ import de.schildbach.wallet.WalletApplication
import de.schildbach.wallet.data.CoinJoinConfig
import de.schildbach.wallet.data.InvitationLinkData
import de.schildbach.wallet.database.dao.UserAlertDao
+import de.schildbach.wallet.database.dao.UsernameRequestDao
import de.schildbach.wallet.database.entity.BlockchainIdentityConfig
import de.schildbach.wallet.database.entity.BlockchainIdentityData
import de.schildbach.wallet.database.entity.BlockchainIdentityData.CreationState
import de.schildbach.wallet.database.entity.DashPayProfile
+import de.schildbach.wallet.database.entity.UsernameRequest
import de.schildbach.wallet.security.SecurityFunctions
import de.schildbach.wallet.security.SecurityGuard
import de.schildbach.wallet.service.CoinJoinMode
@@ -37,12 +39,18 @@ import org.dash.wallet.common.services.analytics.AnalyticsService
import org.dash.wallet.common.services.analytics.AnalyticsTimer
import org.dashj.platform.dapiclient.model.GrpcExceptionInfo
import org.dashj.platform.dashpay.BlockchainIdentity
+import org.dashj.platform.dashpay.UsernameInfo
+import org.dashj.platform.dashpay.UsernameRequestStatus
+import org.dashj.platform.dashpay.UsernameStatus
+import org.dashj.platform.dashpay.callback.WalletSignerCallback
import org.dashj.platform.dpp.errors.ConcensusErrorMetadata
import org.dashj.platform.dpp.errors.concensus.ConcensusException
import org.dashj.platform.dpp.errors.concensus.basic.identity.IdentityAssetLockTransactionOutPointAlreadyExistsException
import org.dashj.platform.dpp.errors.concensus.basic.identity.InvalidInstantAssetLockProofSignatureException
import org.dashj.platform.dpp.identity.Identity
+import org.dashj.platform.sdk.platform.DomainDocument
import org.dashj.platform.sdk.platform.Names
+import org.dashj.platform.wallet.IdentityVerify
import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@@ -139,6 +147,7 @@ class CreateIdentityService : LifecycleService() {
@Inject lateinit var blockchainIdentityDataDao: BlockchainIdentityConfig
@Inject lateinit var securityFunctions: SecurityFunctions
@Inject lateinit var coinJoinConfig: CoinJoinConfig
+ @Inject lateinit var usernameRequestDao: UsernameRequestDao
private lateinit var securityGuard: SecurityGuard
private val wakeLock by lazy {
@@ -202,7 +211,7 @@ class CreateIdentityService : LifecycleService() {
val blockchainIdentityData = runBlocking {
platformRepo.loadBlockchainIdentityBaseData()
}
- if (blockchainIdentityData != null && blockchainIdentityData.creationState != CreationState.DONE) {
+ if (blockchainIdentityData != null && blockchainIdentityData.creationState != CreationState.DONE && !blockchainIdentityData.restoring) {
handleCreateIdentityAction(null)
}
@@ -288,7 +297,6 @@ class CreateIdentityService : LifecycleService() {
username,
null,
false,
- requestedUsername = blockchainIdentityDataBase?.requestedUsername, // move these back to dashpayconfig?
verificationLink = blockchainIdentityDataBase?.verificationLink
)
platformRepo.updateBlockchainIdentityData(blockchainIdentityData)
@@ -561,9 +569,6 @@ class CreateIdentityService : LifecycleService() {
}
private suspend fun finishRegistration(blockchainIdentity: BlockchainIdentity, encryptionKey: KeyParameter) {
- // TODO: let's delay to make sure that the identity is propagated
- // delay(20000)
-
// This Step is obsolete, verification is handled by the previous block, lets leave it in for now
if (blockchainIdentityData.creationState <= CreationState.IDENTITY_REGISTERED) {
platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.IDENTITY_REGISTERED)
@@ -593,7 +598,7 @@ class CreateIdentityService : LifecycleService() {
//
platformRepo.updateBlockchainIdentityData(blockchainIdentityData, blockchainIdentity)
}
- // delay(30000) // 20 sec delay for platform sync
+
if (blockchainIdentityData.creationState <= CreationState.USERNAME_REGISTERING) {
platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.USERNAME_REGISTERING)
//
@@ -613,16 +618,10 @@ class CreateIdentityService : LifecycleService() {
analytics.logEvent(AnalyticsConstants.UsersContacts.CREATE_USERNAME, mapOf())
}
- // Step 6: A profile will not be created, since the user has not yet specified
- // a display name, public message (bio) or an avatarUrl
- // However, a default empty profile will be saved to the local database.
- val emptyProfile = DashPayProfile(blockchainIdentity.uniqueIdString, blockchainIdentity.currentUsername!!)
- platformRepo.updateDashPayProfile(emptyProfile)
-
addInviteUserAlert()
// check for contested username
- if (blockchainIdentityData.requestedUsername != null && Names.isUsernameContestable(blockchainIdentityData.requestedUsername!!)) {
+ if (Names.isUsernameContestable(blockchainIdentityData.username!!)) {
// get the createdAt date to estimate 2 week voting period
// check that this username is contested and up for a vote
// save the verification link in a new document
@@ -630,9 +629,51 @@ class CreateIdentityService : LifecycleService() {
if (blockchainIdentityData.creationState <= CreationState.REQUESTED_NAME_CHECKING) {
platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.REQUESTED_NAME_CHECKING)
+
+ val contenders = platformRepo.platform.names.getVoteContenders(blockchainIdentityData.username!!)
+
blockchainIdentity.currentVotingPeriodStarts = System.currentTimeMillis()
blockchainIdentity.currentUsernameRequested = true
+ if (contenders.isEmpty()) {
+ error("${blockchainIdentityData.username} not found because there are no contenders")
+ }
+
+ val documentWithVotes = contenders.map[blockchainIdentity.uniqueIdentifier]
+ ?: error("${blockchainIdentityData.username} does not have ${blockchainIdentity.uniqueIdentifier} as a contender")
+
+ val document = DomainDocument(
+ platformRepo.platform.names.deserialize(documentWithVotes.seralizedDocument)
+ )
+ blockchainIdentityData.verificationLink?.let { verificationLink ->
+ if (verificationLink.startsWith("https://") || verificationLink.startsWith("http://")) {
+ IdentityVerify(platformRepo.platform.platform).createForDashDomain(
+ blockchainIdentityData.username!!,
+ verificationLink,
+ blockchainIdentity.identity!!,
+ WalletSignerCallback(walletApplication.wallet!!, encryptionKey)
+ )
+ }
+ }
+
+ usernameRequestDao.insert(
+ UsernameRequest(
+ blockchainIdentity.uniqueIdString + " " + blockchainIdentityData.username!!,
+ blockchainIdentityData.username!!,
+ Names.normalizeString(blockchainIdentityData.username!!),
+ document.createdAt!! / 1000,
+ blockchainIdentity.uniqueIdString,
+ blockchainIdentityData.verificationLink,
+ documentWithVotes.votes,
+ contenders.lockVoteTally,
+ false
+ )
+ )
+
+ val usernameInfo = blockchainIdentity.usernameStatuses[blockchainIdentity.currentUsername!!]!!
+ usernameInfo.votingStartedAt = document.createdAt
+ usernameInfo.requestStatus = UsernameRequestStatus.VOTING
+
platformRepo.updateBlockchainIdentityData(blockchainIdentityData, blockchainIdentity)
}
@@ -658,6 +699,12 @@ class CreateIdentityService : LifecycleService() {
platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.DONE)
}
+ // Step 6: A profile will not be created, since the user has not yet specified
+ // a display name, public message (bio) or an avatarUrl
+ // However, a default empty profile will be saved to the local database.
+ val emptyProfile = DashPayProfile(blockchainIdentity.uniqueIdString, blockchainIdentity.currentUsername!!)
+ platformRepo.updateDashPayProfile(emptyProfile)
+
platformRepo.init()
platformSyncService.initSync()
@@ -769,6 +816,53 @@ class CreateIdentityService : LifecycleService() {
platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.REQUESTED_NAME_CHECKING)
// check if the network has this name in the queue for voting
+ val contestedNames = platformRepo.platform.names.getContestedNames()
+
+ contestedNames.forEach { name ->
+ val voteContenders = platformRepo.platform.names.getVoteContenders(name)
+ voteContenders.map.forEach { (identifier, documentWithVotes) ->
+ if (blockchainIdentity.uniqueIdentifier == identifier) {
+ blockchainIdentity.currentUsername = name
+ // load the serialized doc to get voting period and status...
+
+ blockchainIdentity.usernameStatuses.apply {
+ clear()
+ val usernameInfo = UsernameInfo(null, UsernameStatus.CONFIRMED, blockchainIdentity.currentUsername!!,
+ UsernameRequestStatus.VOTING, 0)
+ put(blockchainIdentity.currentUsername!!, usernameInfo)
+ }
+
+ val contestedDocument = DomainDocument(
+ platformRepo.platform.names.deserialize(documentWithVotes.seralizedDocument)
+ )
+ blockchainIdentity.currentUsername = contestedDocument.label
+
+ val verifyDocument = IdentityVerify(platformRepo.platform.platform).get(
+ blockchainIdentity.uniqueIdentifier,
+ name
+ )
+
+ usernameRequestDao.insert(
+ UsernameRequest(
+ UsernameRequest.getRequestId(blockchainIdentity.uniqueIdString, blockchainIdentity.currentUsername!!),
+ contestedDocument.label,
+ contestedDocument.normalizedLabel,
+ contestedDocument.createdAt!! / 1000,
+ blockchainIdentity.uniqueIdString,
+ verifyDocument?.url, // get it from the document
+ documentWithVotes.votes,
+ voteContenders.lockVoteTally,
+ false
+ )
+ )
+ val usernameInfo = blockchainIdentity.usernameStatuses[blockchainIdentity.currentUsername!!]!!
+ usernameInfo.votingStartedAt = contestedDocument.createdAt
+ usernameInfo.requestStatus = UsernameRequestStatus.VOTING
+ }
+ }
+ }
+
+
platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.REQUESTED_NAME_CHECKED)
platformRepo.updateBlockchainIdentityData(blockchainIdentityData, blockchainIdentity)
@@ -782,25 +876,28 @@ class CreateIdentityService : LifecycleService() {
platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.REQUESTED_NAME_CHECKED)
platformRepo.updateBlockchainIdentityData(blockchainIdentityData, blockchainIdentity)
+ platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.VOTING)
+ platformRepo.updateBlockchainIdentityData(blockchainIdentityData, blockchainIdentity)
}
//
// Step 6: Find the profile
//
- platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.DASHPAY_PROFILE_CREATING)
platformRepo.recoverDashPayProfile(blockchainIdentity)
// blockchainIdentity hasn't changed
- platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.DASHPAY_PROFILE_CREATED)
platformSyncService.updateSyncStatus(PreBlockStage.GetProfile)
addInviteUserAlert()
// We are finished recovering
- platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.DONE)
blockchainIdentityData.finishRestoration()
+ if (blockchainIdentityData.creationState != CreationState.VOTING) {
+ platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.DONE)
+ platformRepo.updateBlockchainIdentityData(blockchainIdentityData)
+ // Complete the entire process
+ platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.DONE_AND_DISMISS)
+ }
platformRepo.updateBlockchainIdentityData(blockchainIdentityData)
- // Complete the entire process
- platformRepo.updateIdentityCreationState(blockchainIdentityData, CreationState.DONE_AND_DISMISS)
platformSyncService.updateSyncStatus(PreBlockStage.RecoveryComplete)
platformRepo.init()
diff --git a/wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt b/wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt
index 5ba4fce12..053fea7b5 100644
--- a/wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt
+++ b/wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt
@@ -24,6 +24,7 @@ import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import de.schildbach.wallet.Constants
+import de.schildbach.wallet.Constants.DASH_PAY_FEE_CONTESTED
import de.schildbach.wallet.WalletApplication
import de.schildbach.wallet.data.*
import de.schildbach.wallet.database.AppDatabase
@@ -61,9 +62,6 @@ import org.dashj.platform.dapiclient.MaxRetriesReachedException
import org.dashj.platform.dapiclient.NoAvailableAddressesForRetryException
import org.dashj.platform.dapiclient.model.GrpcExceptionInfo
import org.dashj.platform.dashpay.*
-import org.dashj.platform.dashpay.BlockchainIdentity.Companion.BLOCKCHAIN_USERNAME_SALT
-import org.dashj.platform.dashpay.BlockchainIdentity.Companion.BLOCKCHAIN_USERNAME_STATUS
-import org.dashj.platform.dashpay.BlockchainIdentity.Companion.BLOCKCHAIN_USERNAME_UNIQUE
import org.dashj.platform.dpp.document.Document
import org.dashj.platform.dpp.errors.concensus.basic.identity.InvalidInstantAssetLockProofException
import org.dashj.platform.dpp.identifier.Identifier
@@ -519,7 +517,15 @@ class PlatformRepo @Inject constructor(
} else {
Constants.DASH_PAY_FEE
}
- val cftx = blockchainIdentity.createAssetLockTransaction(fee, keyParameter, useCoinJoin, true)
+ val balance = walletApplication.wallet!!.getBalance(Wallet.BalanceType.ESTIMATED_SPENDABLE)
+ val emptyWallet = balance == fee && balance <= (fee + Transaction.MIN_NONDUST_OUTPUT)
+ val cftx = blockchainIdentity.createAssetLockTransaction(
+ fee,
+ keyParameter,
+ useCoinJoin,
+ returnChange = true,
+ emptyWallet = emptyWallet
+ )
blockchainIdentity.initializeAssetLockTransaction(cftx)
}
}
@@ -527,7 +533,15 @@ class PlatformRepo @Inject constructor(
suspend fun createTopupTransactionAsync(blockchainIdentity: BlockchainIdentity, topupAmount: Coin, keyParameter: KeyParameter?, useCoinJoin: Boolean) {
withContext(Dispatchers.IO) {
Context.propagate(walletApplication.wallet!!.context)
- val cftx = blockchainIdentity.createTopupFundingTransaction(topupAmount, keyParameter, useCoinJoin, true)
+ val balance = walletApplication.wallet!!.getBalance(Wallet.BalanceType.ESTIMATED_SPENDABLE)
+ val emptyWallet = balance == topupAmount && balance <= (topupAmount + Transaction.MIN_NONDUST_OUTPUT)
+ val cftx = blockchainIdentity.createTopupFundingTransaction(
+ topupAmount,
+ keyParameter,
+ useCoinJoin,
+ returnChange = true,
+ emptyWallet = emptyWallet
+ )
blockchainIdentity.initializeAssetLockTransaction(cftx)
}
}
@@ -586,7 +600,7 @@ class PlatformRepo @Inject constructor(
suspend fun recoverIdentityAsync(blockchainIdentity: BlockchainIdentity, publicKeyHash: ByteArray) {
withContext(Dispatchers.IO) {
- blockchainIdentity.registrationStatus = BlockchainIdentity.RegistrationStatus.UNKNOWN
+ blockchainIdentity.registrationStatus = IdentityStatus.UNKNOWN
blockchainIdentity.recoverIdentity(publicKeyHash)
}
}
@@ -609,7 +623,7 @@ class PlatformRepo @Inject constructor(
@Deprecated("watch* functions should no longer be used")
suspend fun isNamePreorderedAsync(blockchainIdentity: BlockchainIdentity) {
withContext(Dispatchers.IO) {
- val set = blockchainIdentity.getUsernamesWithStatus(BlockchainIdentity.UsernameStatus.PREORDER_REGISTRATION_PENDING)
+ val set = blockchainIdentity.getUsernamesWithStatus(UsernameStatus.PREORDER_REGISTRATION_PENDING)
val saltedDomainHashes = blockchainIdentity.saltedDomainHashesForUsernames(set)
val (result, usernames) = blockchainIdentity.watchPreorder(saltedDomainHashes, 100, 1000, RetryDelayType.SLOW20)
if (!result) {
@@ -636,7 +650,7 @@ class PlatformRepo @Inject constructor(
@Deprecated("watch* functions should no longer be used")
suspend fun isNameRegisteredAsync(blockchainIdentity: BlockchainIdentity) {
withContext(Dispatchers.IO) {
- val (result, usernames) = blockchainIdentity.watchUsernames(blockchainIdentity.getUsernamesWithStatus(BlockchainIdentity.UsernameStatus.REGISTRATION_PENDING), 100, 1000, RetryDelayType.SLOW20)
+ val (result, usernames) = blockchainIdentity.watchUsernames(blockchainIdentity.getUsernamesWithStatus(UsernameStatus.REGISTRATION_PENDING), 100, 1000, RetryDelayType.SLOW20)
if (!result) {
throw TimeoutException("the usernames: $usernames were not found to be registered in the allotted amount of time")
}
@@ -688,18 +702,16 @@ class PlatformRepo @Inject constructor(
// Syncing complete
return blockchainIdentity.apply {
currentUsername = blockchainIdentityData.username
- registrationStatus = blockchainIdentityData.registrationStatus ?: BlockchainIdentity.RegistrationStatus.NOT_REGISTERED
- val usernameStatus = HashMap()
+ registrationStatus = blockchainIdentityData.registrationStatus ?: IdentityStatus.NOT_REGISTERED
// usernameStatus, usernameSalts are not set if preorder hasn't started
if (blockchainIdentityData.creationState >= BlockchainIdentityData.CreationState.PREORDER_REGISTERING) {
- if (blockchainIdentityData.preorderSalt != null) {
- usernameStatus[BLOCKCHAIN_USERNAME_SALT] = blockchainIdentityData.preorderSalt!!
- usernameSalts[currentUsername!!] = blockchainIdentityData.preorderSalt!!
- }
- if (blockchainIdentityData.usernameStatus != null) {
- usernameStatus[BLOCKCHAIN_USERNAME_STATUS] = blockchainIdentityData.usernameStatus!!
- }
- usernameStatus[BLOCKCHAIN_USERNAME_UNIQUE] = true
+ var usernameStatus = UsernameInfo(
+ blockchainIdentityData.preorderSalt,
+ blockchainIdentityData.usernameStatus ?: UsernameStatus.NOT_PRESENT,
+ currentUsername,
+ blockchainIdentityData.usernameRequested,
+ blockchainIdentityData.votingPeriodStart
+ )
currentUsername ?.let {
usernameStatuses[it] = usernameStatus
}
@@ -712,24 +724,22 @@ class PlatformRepo @Inject constructor(
suspend fun updateBlockchainIdentityData(blockchainIdentityData: BlockchainIdentityData, blockchainIdentity: BlockchainIdentity) {
blockchainIdentityData.apply {
creditFundingTxId = blockchainIdentity.assetLockTransaction?.txId
- userId = if (blockchainIdentity.registrationStatus == BlockchainIdentity.RegistrationStatus.REGISTERED)
+ userId = if (blockchainIdentity.registrationStatus == IdentityStatus.REGISTERED)
blockchainIdentity.uniqueIdString
else null
identity = blockchainIdentity.identity
registrationStatus = blockchainIdentity.registrationStatus
if (blockchainIdentity.currentUsername != null) {
username = blockchainIdentity.currentUsername
- if (blockchainIdentity.registrationStatus == BlockchainIdentity.RegistrationStatus.REGISTERED) {
+ if (blockchainIdentity.registrationStatus == IdentityStatus.REGISTERED) {
preorderSalt = blockchainIdentity.saltForUsername(blockchainIdentity.currentUsername!!, false)
usernameStatus = blockchainIdentity.statusOfUsername(blockchainIdentity.currentUsername!!)
}
+ usernameRequested = blockchainIdentity.getUsernameRequestStatus(username!!)
+ votingPeriodStart = blockchainIdentity.getUsernameVotingStart(username!!)
}
creditBalance = blockchainIdentity.creditBalance
- //activeKeyCount = blockchainIdentity.activeKeyCount
- //totalKeyCount = blockchainIdentity.totalKeyCount
- //keysCreated = blockchainIdentity.keysCreated
- //currentMainKeyIndex = blockchainIdentity.currentMainKeyIndex
- //currentMainKeyType = blockchainIdentity.currentMainKeyType
+
}
updateBlockchainIdentityData(blockchainIdentityData)
}
@@ -976,11 +986,15 @@ class PlatformRepo @Inject constructor(
// dashj Context does not work with coroutines well, so we need to call Context.propogate
// in each suspend method that uses the dashj Context
Context.propagate(walletApplication.wallet!!.context)
+ val balance = walletApplication.wallet!!.getBalance(Wallet.BalanceType.ESTIMATED_SPENDABLE)
+ val fee = DASH_PAY_FEE_CONTESTED
+ val emptyWallet = balance == fee && balance <= (fee + Transaction.MIN_NONDUST_OUTPUT)
val cftx = blockchainIdentity.createInviteFundingTransaction(
Constants.DASH_PAY_FEE,
keyParameter,
useCoinJoin = coinJoinConfig.getMode() != CoinJoinMode.NONE,
- returnChange = true
+ returnChange = true,
+ emptyWallet = emptyWallet
)
val invitation = Invitation(cftx.identityId.toStringBase58(), cftx.txId,
System.currentTimeMillis())
diff --git a/wallet/src/de/schildbach/wallet/ui/dashpay/work/BroadcastIdentityVerifyOperation.kt b/wallet/src/de/schildbach/wallet/ui/dashpay/work/BroadcastIdentityVerifyOperation.kt
new file mode 100644
index 000000000..abc77b112
--- /dev/null
+++ b/wallet/src/de/schildbach/wallet/ui/dashpay/work/BroadcastIdentityVerifyOperation.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024 Dash Core Group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package de.schildbach.wallet.ui.dashpay.work
+
+import android.annotation.SuppressLint
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.liveData
+import androidx.lifecycle.switchMap
+import androidx.work.*
+import de.schildbach.wallet.livedata.Resource
+import de.schildbach.wallet.security.SecurityGuard
+import org.dash.wallet.common.services.analytics.AnalyticsService
+import org.slf4j.LoggerFactory
+
+class BroadcastIdentityVerifyOperation(val application: Application) {
+
+ class SendContactRequestOperationException(message: String) : java.lang.Exception(message)
+
+ companion object {
+ private val log = LoggerFactory.getLogger(BroadcastIdentityVerifyOperation::class.java)
+
+ private const val WORK_NAME = "BroadcastIdentityVerify.WORK#"
+
+ fun uniqueWorkName(toUserId: String) = WORK_NAME + toUserId
+ }
+
+ private val workManager: WorkManager = WorkManager.getInstance(application)
+
+ /**
+ * Gets the list of all SendContactRequestWorker WorkInfo's
+ */
+ val allOperationsData = workManager.getWorkInfosByTagLiveData(SendContactRequestWorker::class.qualifiedName!!)
+
+ @SuppressLint("EnqueueWork")
+ fun create(username: String, url: String): WorkContinuation {
+
+ val password = SecurityGuard().retrievePassword()
+ val sendContactRequestWorker = OneTimeWorkRequestBuilder()
+ .setInputData(workDataOf(
+ BroadcastIdentityVerifyWorker.KEY_PASSWORD to password,
+ BroadcastIdentityVerifyWorker.KEY_USERNAME to username,
+ BroadcastIdentityVerifyWorker.KEY_URL to url
+ )
+ )
+ .addTag("username:$username")
+ .build()
+
+ return WorkManager.getInstance(application)
+ .beginUniqueWork(uniqueWorkName(username),
+ ExistingWorkPolicy.KEEP,
+ sendContactRequestWorker)
+ }
+}
\ No newline at end of file
diff --git a/wallet/src/de/schildbach/wallet/ui/dashpay/work/BroadcastIdentityVerifyWorker.kt b/wallet/src/de/schildbach/wallet/ui/dashpay/work/BroadcastIdentityVerifyWorker.kt
new file mode 100644
index 000000000..2ef02b51a
--- /dev/null
+++ b/wallet/src/de/schildbach/wallet/ui/dashpay/work/BroadcastIdentityVerifyWorker.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024 Dash Core Group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package de.schildbach.wallet.ui.dashpay.work
+
+import android.content.Context
+import androidx.hilt.work.HiltWorker
+import androidx.work.Data
+import androidx.work.WorkerParameters
+import androidx.work.workDataOf
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedInject
+import de.schildbach.wallet.WalletApplication
+import de.schildbach.wallet.service.platform.PlatformBroadcastService
+import de.schildbach.wallet.ui.dashpay.PlatformRepo
+import org.bitcoinj.crypto.KeyCrypterException
+import org.bouncycastle.crypto.params.KeyParameter
+import org.dash.wallet.common.WalletDataProvider
+import org.dash.wallet.common.services.analytics.AnalyticsService
+
+@HiltWorker
+class BroadcastIdentityVerifyWorker @AssistedInject constructor(
+ @Assisted context: Context,
+ @Assisted parameters: WorkerParameters,
+ val analytics: AnalyticsService,
+ val platformBroadcastService: PlatformBroadcastService,
+ val walletDataProvider: WalletDataProvider
+) : BaseWorker(context, parameters) {
+
+ companion object {
+ const val KEY_PASSWORD = "BroadcastIdentityVerifyWorker.PASSWORD"
+ const val KEY_USERNAME = "BroadcastIdentityVerifyWorker.USERNAME"
+ const val KEY_URL = "BroadcastIdentityVerifyWorker.URL"
+ }
+
+ override suspend fun doWorkWithBaseProgress(): Result {
+ val password = inputData.getString(KEY_PASSWORD)
+ ?: return Result.failure(workDataOf(KEY_ERROR_MESSAGE to "missing KEY_PASSWORD parameter"))
+ val username = inputData.getString(KEY_USERNAME)
+ ?: return Result.failure(workDataOf(KEY_ERROR_MESSAGE to "missing KEY_USERNAME parameter"))
+ val url = inputData.getString(KEY_URL)
+ ?: return Result.failure(workDataOf(KEY_ERROR_MESSAGE to "missing KEY_URL parameter"))
+
+ val encryptionKey: KeyParameter
+ try {
+ encryptionKey = walletDataProvider.wallet!!.keyCrypter!!.deriveKey(password)
+ } catch (ex: KeyCrypterException) {
+ analytics.logError(ex, "Identity Verify: failed to derive encryption key")
+ val msg = formatExceptionMessage("derive encryption key", ex)
+ return Result.failure(workDataOf(KEY_ERROR_MESSAGE to msg))
+ }
+
+ return try {
+ val identityVerifyDocument = platformBroadcastService.broadcastIdentityVerify(username, url, encryptionKey)
+ Result.success(workDataOf(
+ KEY_USERNAME to identityVerifyDocument.normalizedLabel,
+ KEY_URL to identityVerifyDocument.url
+ ))
+ } catch (ex: Exception) {
+ analytics.logError(ex, "Identity Verify: failed to broadcast identity verify document")
+ Result.failure(workDataOf(
+ KEY_ERROR_MESSAGE to formatExceptionMessage("broadcast identity verify", ex)))
+ }
+ }
+}
\ No newline at end of file
diff --git a/wallet/src/de/schildbach/wallet/ui/invite/CreateInviteViewModel.kt b/wallet/src/de/schildbach/wallet/ui/invite/CreateInviteViewModel.kt
index 581a7d15e..b73bc6dab 100644
--- a/wallet/src/de/schildbach/wallet/ui/invite/CreateInviteViewModel.kt
+++ b/wallet/src/de/schildbach/wallet/ui/invite/CreateInviteViewModel.kt
@@ -66,7 +66,7 @@ class CreateInviteViewModel @Inject constructor(
private fun combineLatestData(): Boolean {
val isSynced = blockchainStateData.value?.isSynced() ?: false
val noIdentityCreatedOrInProgress = (blockchainIdentity.value == null) || blockchainIdentity.value!!.creationState == BlockchainIdentityData.CreationState.NONE
- return isSynced && !noIdentityCreatedOrInProgress //&& canAffordIdentityCreation()
+ return isSynced && !noIdentityCreatedOrInProgress
}
val invitationsLiveData = invitationsDao.observe().asLiveData()
diff --git a/wallet/src/de/schildbach/wallet/ui/main/MainActivity.kt b/wallet/src/de/schildbach/wallet/ui/main/MainActivity.kt
index bb10dc912..00efdcfbc 100644
--- a/wallet/src/de/schildbach/wallet/ui/main/MainActivity.kt
+++ b/wallet/src/de/schildbach/wallet/ui/main/MainActivity.kt
@@ -33,11 +33,13 @@ import android.view.MenuItem
import android.view.WindowManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
+import androidx.annotation.NavigationRes
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope
+import androidx.navigation.findNavController
import com.google.common.collect.ImmutableList
import dagger.hilt.android.AndroidEntryPoint
import de.schildbach.wallet.Constants
@@ -91,9 +93,19 @@ class MainActivity : AbstractBindServiceActivity(), ActivityCompat.OnRequestPerm
const val EXTRA_RESET_BLOCKCHAIN = "reset_blockchain"
private const val EXTRA_INVITE = "extra_invite"
+ private const val EXTRA_NAVIGATION_DESTINATION = "extra_destination"
fun createIntent(context: Context): Intent {
- return Intent(context, MainActivity::class.java)
+ return Intent(context, MainActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
+ }
+
+ fun createIntent(context: Context, @NavigationRes destination: Int): Intent {
+ return Intent(context, MainActivity::class.java).apply {
+ putExtra(EXTRA_NAVIGATION_DESTINATION, destination)
+ addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
}
fun createIntent(context: Context, invite: InvitationLinkData): Intent {
@@ -385,6 +397,11 @@ class MainActivity : AbstractBindServiceActivity(), ActivityCompat.OnRequestPerm
pendingInvite = invite
}
}
+ if (intent.hasExtra(EXTRA_NAVIGATION_DESTINATION)) {
+ val destination = intent.extras!!.getInt(EXTRA_NAVIGATION_DESTINATION)
+ val navController = findNavController(R.id.nav_host_fragment)
+ navController.navigate(destination)
+ }
val action = intent.action
val extras = intent.extras
if (NfcAdapter.ACTION_NDEF_DISCOVERED == action) {
diff --git a/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt b/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt
index d8add773f..245bb7972 100644
--- a/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt
+++ b/wallet/src/de/schildbach/wallet/ui/main/MainViewModel.kt
@@ -697,7 +697,7 @@ class MainViewModel @Inject constructor(
}
suspend fun getRequestedUsername(): String =
- blockchainIdentityDataDao.get(BlockchainIdentityConfig.REQUESTED_USERNAME) ?: ""
+ blockchainIdentityDataDao.get(BlockchainIdentityConfig.USERNAME) ?: ""
suspend fun getInviteHistory() = invitationsDao.loadAll()
private fun combineLatestData(): Boolean {
@@ -708,8 +708,7 @@ class MainViewModel @Inject constructor(
val isSynced = _isBlockchainSynced.value ?: false
val noIdentityCreatedOrInProgress =
(blockchainIdentity.value == null) || blockchainIdentity.value!!.creationState == BlockchainIdentityData.CreationState.NONE
- val canAffordIdentityCreation = walletData.canAffordIdentityCreation()
- return isSynced && isPlatformAvailable && noIdentityCreatedOrInProgress //&& canAffordIdentityCreation
+ return isSynced && isPlatformAvailable && noIdentityCreatedOrInProgress
}
}
diff --git a/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt b/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt
index 2a6031136..0c1551c8b 100644
--- a/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt
+++ b/wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt
@@ -20,6 +20,7 @@ package de.schildbach.wallet.ui.more
import android.content.Intent
import android.graphics.drawable.AnimationDrawable
import android.os.Bundle
+import android.text.format.DateFormat
import android.view.View
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
@@ -60,6 +61,7 @@ import org.dash.wallet.common.ui.viewBinding
import org.dash.wallet.common.util.observe
import org.dash.wallet.common.util.safeNavigate
import org.slf4j.LoggerFactory
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
@AndroidEntryPoint
@@ -180,6 +182,12 @@ class MoreFragment : Fragment(R.layout.fragment_more) {
binding.joinDashpayContainer.visibility = View.GONE
binding.requestedUsernameContainer.visibility = View.VISIBLE
binding.requestedUsernameTitle.text = mainActivityViewModel.getRequestedUsername()
+ val votingPeriod = it.votingPeriodStart?.let { startTime ->
+ val endTime = startTime + TimeUnit.MILLISECONDS.toDays(14)
+ val dateFormat = DateFormat.getMediumDateFormat(requireContext())
+ String.format("%s - %s", dateFormat.format(startTime), dateFormat.format(endTime))
+ } ?: "Voting Period not found"
+ binding.requestedUsernameSubtitleTwo.text = getString(R.string.requested_voting_duration, votingPeriod)
} else {
binding.joinDashpayContainer.visibility = View.VISIBLE
binding.requestedUsernameContainer.visibility = View.GONE
diff --git a/wallet/src/de/schildbach/wallet/ui/username/UsernameRegistrationFragment.kt b/wallet/src/de/schildbach/wallet/ui/username/UsernameRegistrationFragment.kt
index 90a478e9a..345bb144d 100644
--- a/wallet/src/de/schildbach/wallet/ui/username/UsernameRegistrationFragment.kt
+++ b/wallet/src/de/schildbach/wallet/ui/username/UsernameRegistrationFragment.kt
@@ -1,5 +1,23 @@
+/*
+ * Copyright 2024 Dash Core Group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
package de.schildbach.wallet.ui.username
+import android.annotation.SuppressLint
import android.graphics.Typeface
import android.graphics.drawable.AnimationDrawable
import android.os.Bundle
@@ -21,28 +39,13 @@ import dagger.hilt.android.AndroidEntryPoint
import de.schildbach.wallet.Constants
import de.schildbach.wallet.WalletApplication
import de.schildbach.wallet.database.entity.BlockchainIdentityData
-import de.schildbach.wallet.data.InvitationLinkData
-import de.schildbach.wallet.livedata.Status
-import de.schildbach.wallet.service.CoinJoinMode
-import de.schildbach.wallet.ui.SetPinActivity
-import de.schildbach.wallet.ui.dashpay.CreateIdentityService
import de.schildbach.wallet.ui.dashpay.DashPayViewModel
-import de.schildbach.wallet.ui.dashpay.PlatformPaymentConfirmDialog
-import de.schildbach.wallet.ui.invite.OnboardFromInviteActivity
+import de.schildbach.wallet.ui.main.MainActivity
import de.schildbach.wallet.ui.username.voting.RequestUserNameViewModel
import de.schildbach.wallet_test.R
-import de.schildbach.wallet_test.databinding.FragmentCreateUsernameBinding
import de.schildbach.wallet_test.databinding.FragmentUsernameRegistrationBinding
-import kotlinx.android.parcel.Parcelize
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.dash.wallet.common.InteractionAwareActivity
-import org.dash.wallet.common.services.analytics.AnalyticsConstants
import org.dash.wallet.common.ui.viewBinding
-import org.dash.wallet.common.util.KeyboardUtil
-import org.dash.wallet.common.util.safeNavigate
@AndroidEntryPoint
@@ -55,7 +58,6 @@ class UsernameRegistrationFragment : Fragment(R.layout.fragment_username_registr
private val binding by viewBinding(FragmentUsernameRegistrationBinding::bind)
private val dashPayViewModel: DashPayViewModel by activityViewModels()
- //private val confirmTransactionSharedViewModel: PlatformPaymentConfirmDialog.SharedViewModel by activityViewModels()
private val requestUsernameViewModel: RequestUserNameViewModel by activityViewModels()
private lateinit var walletApplication: WalletApplication
@@ -66,29 +68,25 @@ class UsernameRegistrationFragment : Fragment(R.layout.fragment_username_registr
private lateinit var checkUsernameNotExistRunnable: Runnable
private var createUsernameArgs: CreateUsernameArgs? = null
-
- //private val regularTypeFace by lazy { ResourcesCompat.getFont(requireContext(), R.font.inter_regular) }
- //private val mediumTypeFace by lazy { ResourcesCompat.getFont(requireContext(), R.font.inter_medium) }
private val slideInAnimation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.slide_in_bottom) }
private val fadeOutAnimation by lazy { AnimationUtils.loadAnimation(requireContext(), R.anim.fade_out) }
private lateinit var completeUsername: String
private var isProcessing = false
+ @SuppressLint("ResourceType")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.chooseUsernameTitle.text = getText(R.string.choose_your_username)
binding.closeBtn.setOnClickListener {
+ if (requestUsernameViewModel.isUsernameContestable()) {
+ startActivity(MainActivity.createIntent(requireContext(), R.id.moreFragment))
+ } else {
+ // go to the home screen
+ startActivity(MainActivity.createIntent(requireContext()))
+ }
requireActivity().finish()
}
-// binding.username.addTextChangedListener(this)
-// binding.registerBtn.setOnClickListener {
-// if (reuseTransaction) {
-// triggerIdentityCreation(true)
-// } else {
-// showConfirmationDialog()
-// }
-// }
binding.processingIdentityDismissBtn.setOnClickListener { requireActivity().finish() }
initViewModel()
@@ -120,45 +118,7 @@ class UsernameRegistrationFragment : Fragment(R.layout.fragment_username_registr
}
}
-// private fun showKeyBoard() {
-// handler.postDelayed({
-// KeyboardUtil.showSoftKeyboard(requireContext(), binding.username)
-// }, 1333)
-// }
-
private fun initViewModel() {
-// confirmTransactionSharedViewModel.clickConfirmButtonEvent.observe(viewLifecycleOwner) {
-// dashPayViewModel.logEvent(AnalyticsConstants.CoinJoinPrivacy.USERNAME_PRIVACY_CONFIRMATION_BTN_CONFIRM)
-// if (createUsernameArgs?.actions == CreateUsernameActions.FROM_INVITE) {
-// triggerIdentityCreationFromInvite(reuseTransaction)
-// } else {
-// triggerIdentityCreation(false)
-// }
-// }
-
-// dashPayViewModel.getUsernameLiveData.observe(viewLifecycleOwner) {
-// (requireActivity() as? InteractionAwareActivity)?.imitateUserInteraction()
-// when (it.status) {
-// Status.LOADING -> {
-// // this is delayed by the logic of checkUsernameNotExist(...) method,
-// // therefore the UI state is configured before calling it using usernameAvailabilityValidationInProgressState()
-// }
-// Status.CANCELED -> {
-// // no need to do anything
-// }
-// Status.ERROR -> {
-// usernameAvailabilityValidationErrorState()
-// }
-// Status.SUCCESS -> {
-// if (it.data != null) {
-// // This user name exists
-// usernameAvailabilityValidationTakenState()
-// } else {
-// usernameAvailabilityValidationAvailableState()
-// }
-// }
-// }
-// }
dashPayViewModel.blockchainIdentity.observe(viewLifecycleOwner) {
when {
it?.creationStateErrorMessage != null -> {
@@ -178,104 +138,11 @@ class UsernameRegistrationFragment : Fragment(R.layout.fragment_username_registr
}
}
-// private fun usernameAvailabilityValidationInProgressState() {
-// binding.usernameExistsReq.visibility = View.VISIBLE
-// binding.usernameExistsReqLabel.visibility = View.VISIBLE
-// binding.usernameExistsReqProgress.visibility = View.VISIBLE
-// binding.usernameExistsReqImg.visibility = View.INVISIBLE
-// binding.usernameExistsReqLabel.visibility = View.VISIBLE
-// binding.usernameExistsReqLabel.typeface = regularTypeFace
-// binding.usernameExistsReqLabel.setTextColor(ResourcesCompat.getColor(resources, R.color.content_primary, null))
-// binding.usernameExistsReqLabel.setText(R.string.identity_username_validating)
-// binding.registerBtn.isEnabled = false
-// }
-//
-// private fun usernameAvailabilityValidationErrorState() {
-// binding.usernameExistsReqProgress.visibility = View.INVISIBLE
-// binding.usernameExistsReqImg.visibility = View.VISIBLE
-// binding.usernameExistsReqImg.setImageResource(R.drawable.ic_username_requirement_x)
-// binding.usernameExistsReqLabel.typeface = mediumTypeFace
-// binding.usernameExistsReqLabel.setTextColor(ResourcesCompat.getColor(resources, R.color.dash_red, null))
-// binding.usernameExistsReqLabel.setText(R.string.platform_communication_error)
-// binding.registerBtn.isEnabled = false
-// }
-//
-// private fun usernameAvailabilityValidationTakenState() {
-// binding.usernameExistsReqProgress.visibility = View.INVISIBLE
-// binding.usernameExistsReqImg.visibility = View.VISIBLE
-// binding.usernameExistsReqImg.setImageResource(R.drawable.ic_username_requirement_x)
-// binding.usernameExistsReqLabel.typeface = mediumTypeFace
-// binding.usernameExistsReqLabel.setTextColor(ResourcesCompat.getColor(resources, R.color.dash_red, null))
-// binding.usernameExistsReqLabel.setText(R.string.identity_username_taken)
-// binding.registerBtn.isEnabled = false
-// }
-//
-// private fun usernameAvailabilityValidationAvailableState() {
-// binding.usernameExistsReqProgress.visibility = View.INVISIBLE
-// binding.usernameExistsReqImg.visibility = View.VISIBLE
-// binding.usernameExistsReqImg.setImageResource(R.drawable.ic_username_requirement_checkmark)
-// binding.usernameExistsReqLabel.typeface = mediumTypeFace
-// binding.usernameExistsReqLabel.setTextColor(ResourcesCompat.getColor(resources, R.color.content_primary, null))
-// binding.usernameExistsReqLabel.setText(R.string.identity_username_available)
-// binding.registerBtn.isEnabled = true
-// }
-
-// private fun triggerIdentityCreation(reuseTransaction: Boolean) {
-// val username = binding.username.text.toString()
-// if (reuseTransaction) {
-// requireActivity().startService(CreateIdentityService.createIntentForNewUsername(requireContext(), username))
-// requireActivity().finish()
-// } else {
-// dashPayViewModel.blockchainIdentity.observe(viewLifecycleOwner) {
-// if (it?.creationStateErrorMessage != null) {
-// requireActivity().finish()
-// } else if (it?.creationState == BlockchainIdentityData.CreationState.DONE) {
-// completeUsername = it.username ?: ""
-// showCompleteState()
-// }
-// }
-// showProcessingState()
-// requireActivity().startService(CreateIdentityService.createIntent(requireContext(), username))
-// }
-// }
-//
-// private fun triggerIdentityCreationFromInvite(reuseTransaction: Boolean) {
-// val username = binding.username.text.toString()
-// if (reuseTransaction) {
-// requireActivity().startService(CreateIdentityService.createIntentFromInviteForNewUsername(requireContext(), username))
-// requireActivity().finish()
-// } else {
-// val fromOnboarding = createUsernameArgs?.fromOnboardng ?: false
-// if (fromOnboarding) {
-// walletApplication.configuration.onboardingInviteUsername = username
-// val goNextIntent = SetPinActivity.createIntent(requireActivity().application, R.string.set_pin_create_new_wallet, false, null, onboardingInvite = true)
-// startActivity(OnboardFromInviteActivity.createIntent(requireContext(), OnboardFromInviteActivity.Mode.STEP_2, goNextIntent))
-// requireActivity().finish()
-// return
-// } else {
-// dashPayViewModel.blockchainIdentity.observe(viewLifecycleOwner) {
-// if (it?.creationStateErrorMessage != null && !reuseTransaction) {
-// requireActivity().finish()
-// } else if (it?.creationState == BlockchainIdentityData.CreationState.DONE) {
-// completeUsername = it.username ?: ""
-// showCompleteState()
-// }
-// }
-// showProcessingState()
-// createUsernameArgs?.invite?.let {
-// requireActivity().startService(CreateIdentityService.createIntentFromInvite(requireContext(), username, it))
-// }
-// }
-// }
-// showProcessingState()
-// }
-
private fun doneAndDismiss() {
dashPayViewModel.usernameDoneAndDismiss()
}
private fun showCompleteState() {
- //binding.registrationContent.visibility = View.GONE
binding.processingIdentity.visibility = View.GONE
binding.chooseUsernameTitle.visibility = View.GONE
binding.orbitView.findViewById(R.id.placeholder_user_icon).visibility = View.GONE
@@ -293,97 +160,6 @@ class UsernameRegistrationFragment : Fragment(R.layout.fragment_username_registr
binding.identityCompleteButton.setOnClickListener { requireActivity().finish() }
}
-// private fun validateUsernameSize(uname: String): Boolean {
-// val lengthValid = uname.length in Constants.USERNAME_MIN_LENGTH..Constants.USERNAME_MAX_LENGTH
-//
-// binding.minCharsReqImg.visibility = if (uname.isNotEmpty()) {
-// binding.minCharsReqLabel.typeface = mediumTypeFace
-// View.VISIBLE
-// } else {
-// binding.minCharsReqLabel.typeface = regularTypeFace
-// View.INVISIBLE
-// }
-//
-// if (lengthValid) {
-// binding.minCharsReqImg.setImageResource(R.drawable.ic_username_requirement_checkmark)
-// } else {
-// binding.minCharsReqImg.setImageResource(R.drawable.ic_username_requirement_x)
-// }
-//
-// return lengthValid
-// }
-
-// private fun validateUsernameCharacters(uname: String): Boolean {
-// val alphaNumHyphenValid = !Regex("[^a-zA-Z0-9\\-]").containsMatchIn(uname)
-// val startOrEndWithHyphen = uname.startsWith("-") || uname.endsWith("-")
-// val containsHyphen = uname.contains("-")
-//
-// binding.alphanumReqImg.visibility = if (uname.isNotEmpty() || !alphaNumHyphenValid) {
-// binding.alphanumReqLabel.typeface = mediumTypeFace
-// View.VISIBLE
-// } else {
-// binding.alphanumReqLabel.typeface = regularTypeFace
-// View.INVISIBLE
-// }
-//
-// if (alphaNumHyphenValid) {
-// binding.alphanumReqImg.setImageResource(R.drawable.ic_username_requirement_checkmark)
-// } else {
-// binding.alphanumReqImg.setImageResource(R.drawable.ic_username_requirement_x)
-// }
-//
-// if (containsHyphen) {
-// binding.hyphenReqImg.visibility = View.VISIBLE
-// binding.hyphenReqLabel.visibility = View.VISIBLE
-// if (!startOrEndWithHyphen) {
-// // leave isValid with the same value that is already has (same as isValid && true)
-// binding.hyphenReqImg.setImageResource(R.drawable.ic_username_requirement_checkmark)
-// binding.hyphenReqLabel.typeface = mediumTypeFace
-// } else {
-// binding.hyphenReqImg.setImageResource(R.drawable.ic_username_requirement_x)
-// binding.hyphenReqLabel.typeface = regularTypeFace
-// }
-// } else {
-// binding.hyphenReqImg.visibility = View.GONE
-// binding.hyphenReqLabel.visibility = View.GONE
-// }
-//
-// return alphaNumHyphenValid && !startOrEndWithHyphen
-// }
-
-// private fun checkUsernameNotExist(username: String) {
-// if (this::checkUsernameNotExistRunnable.isInitialized) {
-// handler.removeCallbacks(checkUsernameNotExistRunnable)
-// }
-// checkUsernameNotExistRunnable = Runnable {
-// dashPayViewModel.searchUsername(username)
-// }
-// handler.postDelayed(checkUsernameNotExistRunnable, 600)
-// }
-//
-// override fun afterTextChanged(s: Editable?) {
-// val username = s?.toString()
-//
-// if (username != null) {
-// var usernameIsValid = validateUsernameCharacters(username) and validateUsernameSize(username) // force validateUsernameSize to execute
-//
-// if (usernameIsValid) { // ensure username meets basic rules before making a Platform query
-// usernameAvailabilityValidationInProgressState()
-// checkUsernameNotExist(username)
-// } else {
-// binding.usernameExistsReqProgress.visibility = View.INVISIBLE
-// binding.usernameExistsReqLabel.visibility = View.GONE
-// binding.usernameExistsReqImg.visibility = View.GONE
-// binding.registerBtn.isEnabled = false
-// if (this::checkUsernameNotExistRunnable.isInitialized) {
-// handler.removeCallbacks(checkUsernameNotExistRunnable)
-// dashPayViewModel.searchUsername(null)
-// }
-// }
-// }
-// (requireActivity() as? InteractionAwareActivity)?.imitateUserInteraction()
-// }
-
private fun showProcessingState() {
if (!isProcessing) {
val username = requestUsernameViewModel.requestedUserName!!
@@ -407,19 +183,4 @@ class UsernameRegistrationFragment : Fragment(R.layout.fragment_username_registr
isProcessing = true
}
}
-
-// private fun showConfirmationDialog() {
-// val upgradeFee = if (createUsernameArgs?.actions == CreateUsernameActions.FROM_INVITE) null else Constants.DASH_PAY_FEE
-// val username = "“${binding. username.text}”"
-// val dialogMessage = getString(R.string.new_account_confirm_message, username)
-// val dialogTitle = getString(R.string.dashpay_upgrade_fee)
-// val dialog = PlatformPaymentConfirmDialog.createDialog(dialogTitle, dialogMessage, upgradeFee, createUsernameArgs?.invite != null)
-// dialog.show(requireActivity().supportFragmentManager, "NewAccountConfirmDialog")
-// }
-
-// override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
-// }
-//
-// override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
-// }
}
diff --git a/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt
index 8cc6acfa0..78195d07b 100644
--- a/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt
+++ b/wallet/src/de/schildbach/wallet/ui/username/UsernameRequestsViewModel.kt
@@ -22,6 +22,7 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import de.schildbach.wallet.database.dao.UsernameRequestDao
import de.schildbach.wallet.database.entity.UsernameRequest
+import de.schildbach.wallet.service.platform.PlatformSyncService
import de.schildbach.wallet.ui.dashpay.utils.DashPayConfig
import de.schildbach.wallet.ui.username.adapters.UsernameRequestGroupView
import kotlinx.coroutines.CoroutineScope
@@ -41,6 +42,7 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.bitcoinj.core.Base58
+import org.dashj.platform.sdk.platform.Names
import java.util.UUID
import javax.inject.Inject
import kotlin.math.min
@@ -71,7 +73,8 @@ data class FiltersUIState(
@HiltViewModel
class UsernameRequestsViewModel @Inject constructor(
private val dashPayConfig: DashPayConfig,
- private val usernameRequestDao: UsernameRequestDao
+ private val usernameRequestDao: UsernameRequestDao,
+ private val platformSyncService: PlatformSyncService
): ViewModel() {
private val workerJob = SupervisorJob()
private val viewModelWorkerScope = CoroutineScope(Dispatchers.IO + workerJob)
@@ -122,6 +125,10 @@ class UsernameRequestsViewModel @Inject constructor(
}
}.onEach { requests -> _uiState.update { it.copy(filteredUsernameRequests = requests) } }
.launchIn(viewModelWorkerScope)
+
+ viewModelWorkerScope.launch {
+ platformSyncService.updateUsernameRequestsWithVotes()
+ }
}
suspend fun setFirstTimeInfoShown() {
@@ -246,81 +253,100 @@ class UsernameRequestsViewModel @Inject constructor(
val from = 1658290321L
viewModelScope.launch {
+ var name = names[Random.nextInt(0, min(names.size, nameCount))]
usernameRequestDao.insert(
UsernameRequest(
UUID.randomUUID().toString(),
- names[Random.nextInt(0, min(names.size, nameCount))],
+ name,
+ Names.normalizeString(name),
Random.nextLong(from, now),
Base58.encode(UUID.randomUUID().toString().toByteArray()),
"https://www.figma.com/file/hh5juOSdGnNNPijJG1NGTi/DashPay%E3%83%BBIn-" +
"process%E3%83%BBAndroid?type=design&node-id=752-11735&mode=design&t=zasn6AKlSwb5NuYS-0",
Random.nextInt(0, 15),
+ Random.nextInt(0, 15),
true
)
)
+ name = names[Random.nextInt(0, min(names.size, nameCount))]
usernameRequestDao.insert(
UsernameRequest(
UUID.randomUUID().toString(),
- names[Random.nextInt(0, min(names.size, nameCount))],
+ name,
+ Names.normalizeString(name),
Random.nextLong(from, now),
Base58.encode(UUID.randomUUID().toString().toByteArray()),
null,
Random.nextInt(0, 15),
+ Random.nextInt(0, 15),
true
)
)
+ name = names[Random.nextInt(0, min(names.size, nameCount))]
usernameRequestDao.insert(
UsernameRequest(
UUID.randomUUID().toString(),
- names[Random.nextInt(0, min(names.size, nameCount))],
+ name,
+ Names.normalizeString(name),
Random.nextLong(from, now),
Base58.encode(UUID.randomUUID().toString().toByteArray()),
null,
Random.nextInt(0, 15),
+ Random.nextInt(0, 15),
false
)
)
+ name = names[Random.nextInt(0, min(names.size, nameCount))]
usernameRequestDao.insert(
UsernameRequest(
UUID.randomUUID().toString(),
- names[Random.nextInt(0, min(names.size, nameCount))],
- Random.nextLong(from, now),
+ name,
+ Names.normalizeString(name), Random.nextLong(from, now),
Base58.encode(UUID.randomUUID().toString().toByteArray()),
"https://twitter.com/ProductHunt/",
Random.nextInt(0, 15),
+ Random.nextInt(0, 15),
false
)
)
+ name = names[Random.nextInt(0, min(names.size, nameCount))]
usernameRequestDao.insert(
UsernameRequest(
UUID.randomUUID().toString(),
- names[Random.nextInt(0, min(names.size, nameCount))],
- Random.nextLong(from, now),
+ name,
+ Names.normalizeString(name), Random.nextLong(from, now),
Base58.encode(UUID.randomUUID().toString().toByteArray()),
null,
Random.nextInt(0, 15),
+ Random.nextInt(0, 15),
false
)
)
+ name = names[Random.nextInt(0, min(names.size, nameCount))]
usernameRequestDao.insert(
UsernameRequest(
UUID.randomUUID().toString(),
- names[Random.nextInt(0, min(names.size, nameCount))],
+ name,
+ Names.normalizeString(name),
Random.nextLong(from, now),
Base58.encode(UUID.randomUUID().toString().toByteArray()),
null,
Random.nextInt(0, 15),
+ Random.nextInt(0, 15),
false
)
)
+ name = names[Random.nextInt(0, min(names.size, nameCount))]
usernameRequestDao.insert(
UsernameRequest(
UUID.randomUUID().toString(),
- names[Random.nextInt(0, min(names.size, nameCount))],
+ name,
+ Names.normalizeString(name),
Random.nextLong(from, now),
Base58.encode(UUID.randomUUID().toString().toByteArray()),
null,
Random.nextInt(0, 15),
+ Random.nextInt(0, 15),
false
)
)
diff --git a/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUserNameViewModel.kt b/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUserNameViewModel.kt
index 41ec3ceee..63e6a5327 100644
--- a/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUserNameViewModel.kt
+++ b/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUserNameViewModel.kt
@@ -21,16 +21,25 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import de.schildbach.wallet.Constants
import de.schildbach.wallet.WalletApplication
+import de.schildbach.wallet.database.dao.UsernameRequestDao
import de.schildbach.wallet.database.entity.BlockchainIdentityConfig
import de.schildbach.wallet.database.entity.BlockchainIdentityConfig.Companion.CREATION_STATE
-import de.schildbach.wallet.database.entity.BlockchainIdentityConfig.Companion.REQUESTED_USERNAME
+import de.schildbach.wallet.database.entity.BlockchainIdentityConfig.Companion.IDENTITY_ID
+import de.schildbach.wallet.database.entity.BlockchainIdentityConfig.Companion.USERNAME
import de.schildbach.wallet.database.entity.BlockchainIdentityData
+import de.schildbach.wallet.database.entity.UsernameRequest
import de.schildbach.wallet.livedata.Status
import de.schildbach.wallet.ui.dashpay.CreateIdentityService
import de.schildbach.wallet.ui.dashpay.PlatformRepo
+import de.schildbach.wallet.ui.dashpay.work.BroadcastIdentityVerifyOperation
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.bitcoinj.core.Coin
@@ -60,7 +69,8 @@ class RequestUserNameViewModel @Inject constructor(
val walletApplication: WalletApplication,
val identityConfig: BlockchainIdentityConfig,
val walletData: WalletDataProvider,
- val platformRepo: PlatformRepo
+ val platformRepo: PlatformRepo,
+ val usernameRequestDao: UsernameRequestDao
) : ViewModel() {
private val _uiState = MutableStateFlow(RequestUserNameUIState())
val uiState: StateFlow = _uiState.asStateFlow()
@@ -73,7 +83,7 @@ class RequestUserNameViewModel @Inject constructor(
val walletBalance: Coin
get() = walletData.getWalletBalance()
suspend fun isUserNameRequested(): Boolean {
- val hasRequestedName = identityConfig.get(REQUESTED_USERNAME).isNullOrEmpty().not()
+ val hasRequestedName = identityConfig.get(USERNAME).isNullOrEmpty().not()
val creationState = BlockchainIdentityData.CreationState.valueOf(
identityConfig.get(CREATION_STATE) ?: BlockchainIdentityData.CreationState.NONE.name
)
@@ -86,11 +96,25 @@ class RequestUserNameViewModel @Inject constructor(
fun canAffordNonContestedUsername(): Boolean = walletBalance >= Constants.DASH_PAY_FEE
fun canAffordContestedUsername(): Boolean = walletBalance >= Constants.DASH_PAY_FEE_CONTESTED
- init {
+ val myUsernameRequest: Flow
+ get() = _myUsernameRequest
+ private val _myUsernameRequest = MutableStateFlow(null)
+ init {
viewModelScope.launch {
_requestedUserNameLink.value = identityConfig.get(BlockchainIdentityConfig.REQUESTED_USERNAME_LINK)
}
+ identityConfig.observe(IDENTITY_ID)
+ .filterNotNull()
+ .onEach {
+ if (requestedUserName == null) {
+ requestedUserName = identityConfig.get(USERNAME)
+ }
+ }
+ .flatMapLatest { usernameRequestDao.observeRequest(UsernameRequest.getRequestId(it, requestedUserName!!)) }
+ .onEach { _myUsernameRequest.value = it }
+ .launchIn(viewModelScope)
+
}
private fun triggerIdentityCreation(reuseTransaction: Boolean) {
@@ -131,7 +155,7 @@ class RequestUserNameViewModel @Inject constructor(
private suspend fun updateConfig() {
requestedUserName?.let { name ->
- identityConfig.set(BlockchainIdentityConfig.REQUESTED_USERNAME, name)
+ identityConfig.set(BlockchainIdentityConfig.USERNAME, name)
}
_requestedUserNameLink.value.let { link ->
identityConfig.set(BlockchainIdentityConfig.REQUESTED_USERNAME_LINK, link ?: "")
@@ -185,6 +209,16 @@ class RequestUserNameViewModel @Inject constructor(
fun verify() {
viewModelScope.launch {
identityConfig.set(BlockchainIdentityConfig.REQUESTED_USERNAME_LINK, _requestedUserNameLink.value ?: "")
+ identityConfig.get(IDENTITY_ID)?.let { identityId ->
+ val usernameRequest = usernameRequestDao.getRequest(
+ UsernameRequest.getRequestId(
+ identityId,
+ requestedUserName!!
+ )
+ )
+ usernameRequest!!.link = _requestedUserNameLink.value
+ usernameRequestDao.update(usernameRequest)
+ }
_uiState.update {
it.copy(
usernameVerified = true
@@ -196,7 +230,7 @@ class RequestUserNameViewModel @Inject constructor(
fun cancelRequest() {
viewModelScope.launch {
- identityConfig.set(BlockchainIdentityConfig.REQUESTED_USERNAME, "")
+ identityConfig.set(BlockchainIdentityConfig.USERNAME, "")
identityConfig.set(BlockchainIdentityConfig.REQUESTED_USERNAME_LINK, "")
identityConfig.set(BlockchainIdentityConfig.CANCELED_REQUESTED_USERNAME_LINK, true)
}
@@ -240,4 +274,13 @@ class RequestUserNameViewModel @Inject constructor(
fun isUsernameContestable(): Boolean {
return Names.isUsernameContestable(requestedUserName!!)
}
+
+ fun publishIdentityVerifyDocument() {
+ _requestedUserNameLink.value?.let { url ->
+ BroadcastIdentityVerifyOperation(walletApplication).create(
+ requestedUserName!!,
+ url
+ )
+ }
+ }
}
diff --git a/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUsernameFragment.kt b/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUsernameFragment.kt
index ff04b8298..530c5b1b1 100644
--- a/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUsernameFragment.kt
+++ b/wallet/src/de/schildbach/wallet/ui/username/voting/RequestUsernameFragment.kt
@@ -125,13 +125,6 @@ class RequestUsernameFragment : Fragment(R.layout.fragment_request_username) {
showKeyboard()
}
-// binding.balanceRequirementDisclaimer.text = getString(
-// R.string.request_username_min_balance_disclaimer,
-// Constants.DASH_PAY_FEE.toPlainString()
-// )
-//
-// binding.balanceRequirementDisclaimer.isVisible = !requestUserNameViewModel.canAffordIdentityCreation()
-
requestUserNameViewModel.uiState.observe(viewLifecycleOwner) {
// if (it.usernameSubmittedSuccess) {
// requireActivity().finish()
diff --git a/wallet/src/de/schildbach/wallet/ui/username/voting/VerifyIdentityFragment.kt b/wallet/src/de/schildbach/wallet/ui/username/voting/VerifyIdentityFragment.kt
index 168b68851..949053c6f 100644
--- a/wallet/src/de/schildbach/wallet/ui/username/voting/VerifyIdentityFragment.kt
+++ b/wallet/src/de/schildbach/wallet/ui/username/voting/VerifyIdentityFragment.kt
@@ -1,10 +1,8 @@
package de.schildbach.wallet.ui.username.voting
import android.os.Bundle
-import android.text.format.DateFormat
import android.view.View
import android.widget.Toast
-import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@@ -21,8 +19,6 @@ import org.dash.wallet.common.ui.viewBinding
import org.dash.wallet.common.util.KeyboardUtil
import org.dash.wallet.common.util.observe
import org.dash.wallet.common.util.safeNavigate
-import java.util.Date
-import java.util.concurrent.TimeUnit
@AndroidEntryPoint
@@ -48,8 +44,8 @@ class VerifyIdentityFragment : Fragment(R.layout.fragment_verfiy_identity) {
}
binding.linkInput.doOnTextChanged { text, _, _, _ ->
- val username = text.toString()
- val isValidLink = username.isNotEmpty()
+ val link = text.toString()
+ val isValidLink = link.startsWith("https://") || link.startsWith("http://")
binding.verifyBtn.isEnabled = isValidLink
if (!text.isNullOrEmpty()) {
binding.linkInputLayout.hint = getString(R.string.link)
@@ -66,7 +62,23 @@ class VerifyIdentityFragment : Fragment(R.layout.fragment_verfiy_identity) {
requestUserNameViewModel.setRequestedUserNameLink(binding.linkInput.text.toString())
requestUserNameViewModel.verify()
lifecycleScope.launch {
- checkViewConfirmDialog()
+ val creationState = dashPayViewModel.blockchainIdentity.value?.creationState ?: BlockchainIdentityData.CreationState.NONE
+ if (creationState.ordinal < BlockchainIdentityData.CreationState.VOTING.ordinal) {
+ checkViewConfirmDialog()
+ dashPayViewModel.blockchainIdentity.observe(viewLifecycleOwner) {
+ if (it?.creationStateErrorMessage != null) {
+ requireActivity().finish()
+ } else {
+ val creationState = it?.creationState ?: BlockchainIdentityData.CreationState.NONE
+ if (creationState.ordinal > BlockchainIdentityData.CreationState.NONE.ordinal) {
+ safeNavigate(VerifyIdentityFragmentDirections.verifyToUsernameRegistrationFragment())
+ }
+ }
+ }
+ } else {
+ requestUserNameViewModel.publishIdentityVerifyDocument()
+ findNavController().popBackStack()
+ }
}
//findNavController().popBackStack()
}
@@ -88,14 +100,6 @@ class VerifyIdentityFragment : Fragment(R.layout.fragment_verfiy_identity) {
// }
// }
}
-
- dashPayViewModel.blockchainIdentity.observe(viewLifecycleOwner) {
- if (it?.creationStateErrorMessage != null) {
- requireActivity().finish()
- } else if ((it?.creationState?.ordinal ?: 0) > BlockchainIdentityData.CreationState.NONE.ordinal) {
- safeNavigate(VerifyIdentityFragmentDirections.verifyToUsernameRegistrationFragment())
- }
- }
}
private fun hideKeyboard() {
diff --git a/wallet/src/de/schildbach/wallet/ui/username/voting/VotingRequestDetailsFragment.kt b/wallet/src/de/schildbach/wallet/ui/username/voting/VotingRequestDetailsFragment.kt
index 9568c284c..1f34b86a4 100644
--- a/wallet/src/de/schildbach/wallet/ui/username/voting/VotingRequestDetailsFragment.kt
+++ b/wallet/src/de/schildbach/wallet/ui/username/voting/VotingRequestDetailsFragment.kt
@@ -19,12 +19,12 @@ package de.schildbach.wallet.ui.username.voting
import android.content.Intent
import android.net.Uri
import android.os.Bundle
+import android.text.format.DateFormat
import android.view.View
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
import de.schildbach.wallet.database.entity.BlockchainIdentityConfig
import de.schildbach.wallet_test.R
import de.schildbach.wallet_test.databinding.FragmentVotingRequestDetailsBinding
@@ -32,28 +32,12 @@ import org.dash.wallet.common.ui.dialogs.AdaptiveDialog
import org.dash.wallet.common.ui.viewBinding
import org.dash.wallet.common.util.observe
import org.dash.wallet.common.util.safeNavigate
+import java.util.concurrent.TimeUnit
class VotingRequestDetailsFragment : Fragment(R.layout.fragment_voting_request_details) {
private val binding by viewBinding(FragmentVotingRequestDetailsBinding::bind)
private val requestUserNameViewModel by activityViewModels()
- override fun onResume() {
- super.onResume()
- // Developer Mode Feature
-
- lifecycleScope.launchWhenResumed {
- binding.username.text =
- requestUserNameViewModel.identityConfig.get(BlockchainIdentityConfig.REQUESTED_USERNAME)
-
- requestUserNameViewModel.requestedUserNameLink.observe(viewLifecycleOwner) {
- it?.let { link ->
- binding.link.text = link
- binding.linkLayout.isVisible = link.isEmpty().not()
- binding.verfiyNowLayout.isVisible = link.isEmpty()
- }
- }
- }
- }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -62,17 +46,34 @@ class VotingRequestDetailsFragment : Fragment(R.layout.fragment_voting_request_d
requireActivity().finish()
}
// TODO Mock identity
- binding.identity.text = "90f95ff7bc2438a748dc8470255b888b2a9ea6837bf518d018dc3d6cddf698"
- binding.votingRange.text = "1 Mar – 15 Mar"
- binding.votesNumber.text = getString(R.string.votes, "10")
- binding.verfiyNow.setOnClickListener {
+
+ binding.verify.setOnClickListener {
safeNavigate(
VotingRequestDetailsFragmentDirections
.votingRequestDetailsFragmentToVerifyIdentityFragment(username = binding.username.text.toString())
)
}
+ requestUserNameViewModel.myUsernameRequest.observe(viewLifecycleOwner) { myUsernameRequest ->
+ binding.username.text = myUsernameRequest?.username
+ binding.identity.text = myUsernameRequest?.identity
+ val votingPeriod = myUsernameRequest?.createdAt?.let { startTime ->
+ val endTime = startTime + TimeUnit.MILLISECONDS.toDays(14)
+ val dateFormat = DateFormat.getMediumDateFormat(requireContext())
+ dateFormat.format(endTime)
+ } ?: "Voting Period not found"
+ binding.votingRange.text = votingPeriod
+ if (myUsernameRequest?.link != null) {
+ binding.link.text = myUsernameRequest.link
+ binding.linkLayout.isVisible = true
+ binding.verfiyNowLayout.isVisible = false
+ } else {
+ binding.linkLayout.isVisible = false
+ binding.verfiyNowLayout.isVisible = true
+ }
+ }
+
binding.linkLayout.setOnClickListener {
requestUserNameViewModel.requestedUserNameLink.value?.let {
@@ -101,21 +102,5 @@ class VotingRequestDetailsFragment : Fragment(R.layout.fragment_voting_request_d
VotingRequestDetailsFragmentDirections.votingRequestDetailsFragmentToUsernameVotingInfoFragment()
)
}
-
- binding.cancelRequestButton.setOnClickListener {
- AdaptiveDialog.create(
- R.drawable.ic_warning,
- title = getString(R.string.do_you_really_want_to_cancel),
- message = getString(R.string.if_you_tap_cancel_request),
- positiveButtonText = getString(R.string.cancel_request),
- negativeButtonText = getString(android.R.string.cancel)
- ).show(requireActivity()) {
- if (it == true) {
- requestUserNameViewModel.cancelRequest()
- requireActivity().finish()
- }
- }
- findNavController().popBackStack()
- }
}
}