feat(sync): add a fallback mechanism when the pairing does't work (#15820)
Fixes #15750 When the pairing fails, the UI now let's the user use the seed phrase instead. When they do, a call is send to the original device and both instances will show an AC notif. When the original device accepts the pairing, the call is made to pair and sync the devices and the AC notifs get deleted
This commit is contained in:
parent
4133afb676
commit
252061d8e8
|
@ -7,6 +7,7 @@ import ../../../../app_service/service/contacts/service as contacts_service
|
|||
import ../../../../app_service/service/message/service as message_service
|
||||
import ../../../../app_service/service/community/service as community_service
|
||||
import ../../../../app_service/service/chat/service as chat_service
|
||||
import ../../../../app_service/service/devices/service as devices_service
|
||||
|
||||
type
|
||||
Controller* = ref object of RootObj
|
||||
|
@ -17,6 +18,7 @@ type
|
|||
messageService: message_service.Service
|
||||
chatService: chat_service.Service
|
||||
communityService: community_service.Service
|
||||
devicesService: devices_service.Service
|
||||
|
||||
proc newController*(
|
||||
delegate: io_interface.AccessInterface,
|
||||
|
@ -26,6 +28,7 @@ proc newController*(
|
|||
messageService: message_service.Service,
|
||||
chatService: chat_service.Service,
|
||||
communityService: community_service.Service,
|
||||
devicesService: devices_service.Service,
|
||||
): Controller =
|
||||
result = Controller()
|
||||
result.delegate = delegate
|
||||
|
@ -35,6 +38,7 @@ proc newController*(
|
|||
result.messageService = messageService
|
||||
result.chatService = chatService
|
||||
result.communityService = communityService
|
||||
result.devicesService = devicesService
|
||||
|
||||
proc delete*(self: Controller) =
|
||||
discard
|
||||
|
@ -162,3 +166,6 @@ proc setActivityCenterReadType*(self: Controller, readType: ActivityCenterReadTy
|
|||
|
||||
proc getActivityCenterReadType*(self: Controller): ActivityCenterReadType =
|
||||
return self.activityCenterService.getActivityCenterReadType()
|
||||
|
||||
proc enableInstallationAndSync*(self: Controller, installationId: string) =
|
||||
self.devicesService.enableInstallationAndSync(installationId)
|
||||
|
|
|
@ -110,3 +110,6 @@ method getActivityCenterReadType*(self: AccessInterface): int {.base.} =
|
|||
|
||||
method setActivityGroupCounters*(self: AccessInterface, counters: Table[ActivityCenterGroup, int]) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
||||
method enableInstallationAndSync*(self: AccessInterface, installationId: string) {.base.} =
|
||||
raise newException(ValueError, "No implementation available")
|
||||
|
|
|
@ -25,6 +25,7 @@ type Item* = ref object
|
|||
repliedMessageItem: MessageItem
|
||||
chatType: ChatType
|
||||
tokenDataItem: TokenDataItem
|
||||
installationId: string
|
||||
|
||||
proc initItem*(
|
||||
id: string,
|
||||
|
@ -43,7 +44,8 @@ proc initItem*(
|
|||
messageItem: MessageItem,
|
||||
repliedMessageItem: MessageItem,
|
||||
chatType: ChatType,
|
||||
tokenDataItem: TokenDataItem
|
||||
tokenDataItem: TokenDataItem,
|
||||
installationId: string
|
||||
): Item =
|
||||
result = Item()
|
||||
result.id = id
|
||||
|
@ -63,6 +65,7 @@ proc initItem*(
|
|||
result.repliedMessageItem = repliedMessageItem
|
||||
result.chatType = chatType
|
||||
result.tokenDataItem = tokenDataItem
|
||||
result.installationId = installationId
|
||||
|
||||
proc `$`*(self: Item): string =
|
||||
result = fmt"""activity_center/Item(
|
||||
|
@ -74,6 +77,7 @@ proc `$`*(self: Item): string =
|
|||
verificationStatus: {$self.verificationStatus.int},
|
||||
sectionId: {$self.sectionId},
|
||||
author: {$self.author},
|
||||
installationId: {$self.installationId},
|
||||
notificationType: {$self.notificationType.int},
|
||||
timestamp: {$self.timestamp},
|
||||
read: {$self.read},
|
||||
|
@ -93,6 +97,9 @@ proc name*(self: Item): string =
|
|||
proc author*(self: Item): string =
|
||||
return self.author
|
||||
|
||||
proc installationId*(self: Item): string =
|
||||
return self.installationId
|
||||
|
||||
proc chatId*(self: Item): string =
|
||||
return self.chatId
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ type
|
|||
RepliedMessage
|
||||
ChatType
|
||||
TokenData
|
||||
InstallationId
|
||||
|
||||
QtObject:
|
||||
type
|
||||
|
@ -89,6 +90,7 @@ QtObject:
|
|||
of NotifRoles.RepliedMessage: result = newQVariant(activityNotificationItem.repliedMessageItem)
|
||||
of NotifRoles.ChatType: result = newQVariant(activityNotificationItem.chatType.int)
|
||||
of NotifRoles.TokenData: result = newQVariant(activityNotificationItem.tokenDataItem)
|
||||
of NotifRoles.InstallationId: result = newQVariant(activityNotificationItem.installationId)
|
||||
|
||||
method roleNames(self: Model): Table[int, string] =
|
||||
{
|
||||
|
@ -109,7 +111,8 @@ QtObject:
|
|||
NotifRoles.Accepted.int: "accepted",
|
||||
NotifRoles.RepliedMessage.int: "repliedMessage",
|
||||
NotifRoles.ChatType.int: "chatType",
|
||||
NotifRoles.TokenData.int: "tokenData"
|
||||
NotifRoles.TokenData.int: "tokenData",
|
||||
NotifRoles.InstallationId.int: "installationId",
|
||||
}.toTable
|
||||
|
||||
proc findNotificationIndex(self: Model, notificationId: string): int =
|
||||
|
|
|
@ -14,6 +14,7 @@ import ../../../../app_service/service/contacts/service as contacts_service
|
|||
import ../../../../app_service/service/message/service as message_service
|
||||
import ../../../../app_service/service/chat/service as chat_service
|
||||
import ../../../../app_service/service/community/service as community_service
|
||||
import ../../../../app_service/service/devices/service as devices_service
|
||||
|
||||
export io_interface
|
||||
|
||||
|
@ -33,7 +34,8 @@ proc newModule*(
|
|||
contactsService: contacts_service.Service,
|
||||
messageService: message_service.Service,
|
||||
chatService: chat_service.Service,
|
||||
communityService: community_service.Service
|
||||
communityService: community_service.Service,
|
||||
devicesService: devices_service.Service,
|
||||
): Module =
|
||||
result = Module()
|
||||
result.delegate = delegate
|
||||
|
@ -46,7 +48,8 @@ proc newModule*(
|
|||
contactsService,
|
||||
messageService,
|
||||
chatService,
|
||||
communityService
|
||||
communityService,
|
||||
devicesService,
|
||||
)
|
||||
result.moduleLoaded = false
|
||||
|
||||
|
@ -216,7 +219,8 @@ method convertToItems*(
|
|||
messageItem,
|
||||
repliedMessageItem,
|
||||
chatDetails.chatType,
|
||||
tokenDataItem
|
||||
tokenDataItem,
|
||||
notification.installationId,
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -323,3 +327,6 @@ method getActivityCenterReadType*(self: Module): int =
|
|||
|
||||
method setActivityGroupCounters*(self: Module, counters: Table[ActivityCenterGroup, int]) =
|
||||
self.view.setActivityGroupCounters(counters)
|
||||
|
||||
method enableInstallationAndSync*(self: Module, installationId: string) =
|
||||
self.controller.enableInstallationAndSync(installationId)
|
||||
|
|
|
@ -211,3 +211,6 @@ QtObject:
|
|||
proc setActivityGroupCounters*(self: View, counters: Table[ActivityCenterGroup, int]) =
|
||||
self.groupCounters = counters
|
||||
self.groupCountersChanged()
|
||||
|
||||
proc enableInstallationAndSync*(self: View, installationId: string) {.slot.} =
|
||||
self.delegate.enableInstallationAndSync(installationId)
|
||||
|
|
|
@ -225,7 +225,7 @@ proc newModule*[T](
|
|||
networkService, tokenService)
|
||||
result.gifsModule = gifs_module.newModule(result, events, gifService)
|
||||
result.activityCenterModule = activity_center_module.newModule(result, events, activityCenterService, contactsService,
|
||||
messageService, chatService, communityService)
|
||||
messageService, chatService, communityService, devicesService)
|
||||
result.communitiesModule = communities_module.newModule(result, events, communityService, contactsService, communityTokensService,
|
||||
networkService, transactionService, tokenService, chatService, walletAccountService, keycardService)
|
||||
result.appSearchModule = app_search_module.newModule(result, events, contactsService, chatService, communityService,
|
||||
|
@ -468,6 +468,9 @@ proc connectForNotificationsOnly[T](self: Module[T]) =
|
|||
self.view.showToastTransactionSendingComplete(args.chainId, args.transactionHash, args.data, args.success,
|
||||
ord(args.txType), args.fromAddress, args.toAddress, args.fromTokenKey, args.fromAmount, args.toTokenKey, args.toAmount)
|
||||
|
||||
self.events.on(SIGNAL_PAIRING_FALLBACK_COMPLETED) do(e:Args):
|
||||
self.view.showToastPairingFallbackCompleted()
|
||||
|
||||
method load*[T](
|
||||
self: Module[T],
|
||||
events: EventEmitter,
|
||||
|
|
|
@ -344,6 +344,7 @@ QtObject:
|
|||
txType: int, fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string, toTokenKey: string, toAmount: string) {.signal.}
|
||||
proc showToastTransactionSendingComplete*(self: View, chainId: int, txHash: string, data: string, success: bool,
|
||||
txType: int, fromAddr: string, toAddr: string, fromTokenKey: string, fromAmount: string, toTokenKey: string, toAmount: string) {.signal.}
|
||||
proc showToastPairingFallbackCompleted*(self: View) {.signal.}
|
||||
|
||||
## Used in test env only, for testing keycard flows
|
||||
proc registerMockedKeycard*(self: View, cardIndex: int, readerState: int, keycardState: int,
|
||||
|
|
|
@ -137,10 +137,6 @@ proc init*(self: Controller) =
|
|||
self.delegate.emitLogOut()
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
handlerId = self.events.onWithUUID(SignalType.NodeReady.event) do(e:Args):
|
||||
self.events.emit("nodeReady", Args())
|
||||
self.connectionIds.add(handlerId)
|
||||
|
||||
handlerId = self.events.onWithUUID(SIGNAL_KEYCARD_RESPONSE) do(e: Args):
|
||||
let args = KeycardLibArgs(e)
|
||||
self.delegate.onKeycardResponse(args.flowType, args.flowEvent)
|
||||
|
@ -642,3 +638,6 @@ proc notificationsNeedsEnable*(self: Controller): bool =
|
|||
|
||||
proc proceedToApp*(self: Controller) =
|
||||
self.delegate.finishAppLoading()
|
||||
|
||||
proc finishPairingThroughSeedPhraseProcess*(self: Controller, installationId: string) =
|
||||
self.devicesService.finishPairingThroughSeedPhraseProcess(installationId)
|
||||
|
|
|
@ -10,3 +10,9 @@ proc delete*(self: SyncDeviceResultState) =
|
|||
|
||||
method executePrimaryCommand*(self: SyncDeviceResultState, controller: Controller) =
|
||||
controller.loginLocalPairingAccount()
|
||||
|
||||
method getNextSecondaryState*(self: SyncDeviceResultState, controller: Controller): State =
|
||||
return createState(StateType.UserProfileEnterSeedPhrase, FlowType.FirstRunOldUserImportSeedPhrase, self)
|
||||
|
||||
method getNextTertiaryState*(self: SyncDeviceResultState, controller: Controller): State =
|
||||
return createState(StateType.SyncDeviceWithSyncCode, FlowType.FirstRunOldUserSyncCode, self)
|
||||
|
|
|
@ -325,6 +325,8 @@ method finishAppLoading*[T](self: Module[T]) =
|
|||
|
||||
method checkFetchingStatusAndProceed*[T](self: Module[T]) =
|
||||
if self.view.fetchingDataModel().isEntityLoaded(FetchingFromWakuProfile):
|
||||
if self.view.getLocalPairingInstallationId() != "":
|
||||
self.controller.finishPairingThroughSeedPhraseProcess(self.view.getLocalPairingInstallationId())
|
||||
self.finishAppLoading()
|
||||
return
|
||||
let currStateObj = self.view.currentStartupStateObj()
|
||||
|
@ -530,6 +532,8 @@ method addToKeycardUidPairsToCheckForAChangeAfterLogin*[T](self: Module[T], oldK
|
|||
|
||||
method removeAllKeycardUidPairsForCheckingForAChangeAfterLogin*[T](self: Module[T]) =
|
||||
self.delegate.removeAllKeycardUidPairsForCheckingForAChangeAfterLogin()
|
||||
if self.view.getLocalPairingInstallationId() != "":
|
||||
self.controller.finishPairingThroughSeedPhraseProcess(self.view.getLocalPairingInstallationId())
|
||||
|
||||
method getConnectionString*[T](self: Module[T]): string =
|
||||
return self.controller.getConnectionString()
|
||||
|
|
|
@ -32,6 +32,8 @@ type ActivityCenterNotificationType* {.pure.}= enum
|
|||
FirstCommunityTokenReceived = 20
|
||||
CommunityBanned = 21
|
||||
CommunityUnbanned = 22
|
||||
NewInstallationReceived = 23
|
||||
NewInstallationCreated = 24
|
||||
|
||||
type ActivityCenterGroup* {.pure.}= enum
|
||||
All = 0,
|
||||
|
@ -65,6 +67,7 @@ type ActivityCenterNotificationDto* = ref object of RootObj
|
|||
verificationStatus*: VerificationStatus
|
||||
name*: string
|
||||
author*: string
|
||||
installationId*: string
|
||||
notificationType*: ActivityCenterNotificationType
|
||||
message*: MessageDto
|
||||
replyMessage*: MessageDto
|
||||
|
@ -84,6 +87,7 @@ proc `$`*(self: ActivityCenterNotificationDto): string =
|
|||
membershipStatus: {self.membershipStatus},
|
||||
contactVerificationStatus: {self.verificationStatus},
|
||||
author: {self.author},
|
||||
installationId: {self.installationId},
|
||||
notificationType: {$self.notificationType.int},
|
||||
timestamp: {self.timestamp},
|
||||
read: {$self.read},
|
||||
|
@ -117,6 +121,7 @@ proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotifica
|
|||
result.verificationStatus = VerificationStatus(verificationStatusInt)
|
||||
|
||||
discard jsonObj.getProp("author", result.author)
|
||||
discard jsonObj.getProp("installationId", result.installationId)
|
||||
|
||||
result.notificationType = ActivityCenterNotificationType.NoType
|
||||
var notificationTypeInt: int
|
||||
|
@ -179,7 +184,9 @@ proc activityCenterNotificationTypesByGroup*(group: ActivityCenterGroup) : seq[i
|
|||
ActivityCenterNotificationType.CommunityTokenReceived.int,
|
||||
ActivityCenterNotificationType.FirstCommunityTokenReceived.int,
|
||||
ActivityCenterNotificationType.CommunityBanned.int,
|
||||
ActivityCenterNotificationType.CommunityUnbanned.int
|
||||
ActivityCenterNotificationType.CommunityUnbanned.int,
|
||||
ActivityCenterNotificationType.NewInstallationReceived.int,
|
||||
ActivityCenterNotificationType.NewInstallationCreated.int,
|
||||
]
|
||||
of ActivityCenterGroup.Mentions:
|
||||
return @[ActivityCenterNotificationType.Mention.int]
|
||||
|
|
|
@ -88,7 +88,7 @@ QtObject:
|
|||
result.chatService = chatService
|
||||
|
||||
proc handleNewNotificationsLoaded(self: Service, activityCenterNotifications: seq[ActivityCenterNotificationDto]) =
|
||||
# For now status-go notify about every notification update regardless active group so we need filter manulay on the desktop side
|
||||
# For now status-go notify about every notification update regardless active group so we need filter manually on the desktop side
|
||||
let groupTypes = activityCenterNotificationTypesByGroup(self.activeGroup)
|
||||
let filteredNotifications = filter(activityCenterNotifications, proc(notification: ActivityCenterNotificationDto): bool =
|
||||
return (self.readType == ActivityCenterReadType.All or not notification.read) and groupTypes.contains(notification.notificationType.int)
|
||||
|
|
|
@ -55,6 +55,8 @@ proc update*(self: LocalPairingStatus, data: LocalPairingEventArgs) =
|
|||
self.chatKey = data.accountData.chatKey
|
||||
of EventReceivedInstallation:
|
||||
self.installation = data.installation
|
||||
of EventCompletedAndNodeReady:
|
||||
self.installation = data.installation
|
||||
of EventReceivedKeystoreFiles:
|
||||
self.transferredKeypairs = data.transferredKeypairs
|
||||
of EventConnectionError:
|
||||
|
|
|
@ -9,6 +9,7 @@ import ./dto/local_pairing_status
|
|||
import app_service/service/settings/service as settings_service
|
||||
import app_service/service/accounts/service as accounts_service
|
||||
import app_service/service/wallet_account/service as wallet_account_service
|
||||
import ../../common/activity_center
|
||||
|
||||
import app/global/global_singleton
|
||||
import app/core/[main]
|
||||
|
@ -49,6 +50,7 @@ const SIGNAL_DEVICES_LOADED* = "devicesLoaded"
|
|||
const SIGNAL_ERROR_LOADING_DEVICES* = "devicesErrorLoading"
|
||||
const SIGNAL_LOCAL_PAIRING_STATUS_UPDATE* = "localPairingStatusUpdate"
|
||||
const SIGNAL_INSTALLATION_NAME_UPDATED* = "installationNameUpdated"
|
||||
const SIGNAL_PAIRING_FALLBACK_COMPLETED* = "pairingFallbackCompleted"
|
||||
|
||||
QtObject:
|
||||
type Service* = ref object of QObject
|
||||
|
@ -174,14 +176,23 @@ QtObject:
|
|||
#
|
||||
|
||||
proc inputConnectionStringForBootstrappingFinished*(self: Service, responseJson: string) {.slot.} =
|
||||
var currentError = ""
|
||||
if self.localPairingStatus.state == LocalPairingState.Error:
|
||||
# The error was already returned by an event, keep it to reuse
|
||||
currentError = self.localPairingStatus.error
|
||||
|
||||
let response = responseJson.parseJson
|
||||
let errorDescription = response["error"].getStr
|
||||
if len(errorDescription) == 0:
|
||||
var installation = InstallationDto()
|
||||
installation.id = response["installationId"].getStr # Set the installation with the ID (only info we have for now)
|
||||
let data = LocalPairingEventArgs(
|
||||
installation: installation,
|
||||
eventType: EventCompletedAndNodeReady,
|
||||
action: ActionPairingInstallation,
|
||||
accountData: LocalPairingAccountData(),
|
||||
error: "")
|
||||
error: currentError,
|
||||
)
|
||||
self.updateLocalPairingStatus(data)
|
||||
return
|
||||
error "failed to start bootstrapping device", errorDescription
|
||||
|
@ -189,7 +200,8 @@ QtObject:
|
|||
eventType: EventConnectionError,
|
||||
action: ActionUnknown,
|
||||
accountData: LocalPairingAccountData(),
|
||||
error: errorDescription)
|
||||
error: errorDescription,
|
||||
)
|
||||
self.updateLocalPairingStatus(data)
|
||||
|
||||
proc validateConnectionString*(self: Service, connectionString: string): string =
|
||||
|
@ -360,3 +372,24 @@ QtObject:
|
|||
configJSON: $configJSON
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
proc finishPairingThroughSeedPhraseProcess*(self: Service, installationId: string) =
|
||||
try:
|
||||
let response = status_installations.finishPairingThroughSeedPhraseProcess(installationId)
|
||||
if response.error != nil:
|
||||
let e = Json.decode($response.error, RpcError)
|
||||
raise newException(CatchableError, e.message)
|
||||
except Exception as e:
|
||||
error "error: ", desription = e.msg
|
||||
|
||||
proc enableInstallationAndSync*(self: Service, installationId: string) =
|
||||
try:
|
||||
let response = status_installations.enableInstallationAndSync(installationId)
|
||||
if response.error != nil:
|
||||
let e = Json.decode($response.error, RpcError)
|
||||
raise newException(CatchableError, e.message)
|
||||
# Parse AC notif
|
||||
checkAndEmitACNotificationsFromResponse(self.events, response.result{"activityCenterNotifications"})
|
||||
self.events.emit(SIGNAL_PAIRING_FALLBACK_COMPLETED, Args())
|
||||
except Exception as e:
|
||||
error "error: ", desription = e.msg
|
||||
|
|
|
@ -35,3 +35,15 @@ proc enableInstallation*(installationId: string): RpcResponse[JsonNode] =
|
|||
proc disableInstallation*(installationId: string): RpcResponse[JsonNode] =
|
||||
let payload = %* [installationId]
|
||||
result = callPrivateRPC("disableInstallation".prefix, payload)
|
||||
|
||||
proc finishPairingThroughSeedPhraseProcess*(installationId: string): RpcResponse[JsonNode] =
|
||||
let payload = %* [{
|
||||
"installationId": installationId,
|
||||
}]
|
||||
result = callPrivateRPC("enableInstallationAndPair".prefix, payload)
|
||||
|
||||
proc enableInstallationAndSync*(installationId: string): RpcResponse[JsonNode] =
|
||||
let payload = %* [{
|
||||
"installationId": installationId,
|
||||
}]
|
||||
result = callPrivateRPC("enableInstallationAndSync".prefix, payload)
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
import mainui.activitycenter.views 1.0
|
||||
import mainui.activitycenter.stores 1.0
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
orientation: Qt.Vertical
|
||||
|
||||
Logs { id: logs }
|
||||
|
||||
QtObject {
|
||||
id: notificationMock
|
||||
|
||||
property string id: "1"
|
||||
property string communityId: "1"
|
||||
property string sectionId: "1"
|
||||
property int notificationType: 1
|
||||
property int timestamp: Date.now()
|
||||
property int previousTimestamp: 0
|
||||
property bool read: false
|
||||
property bool dismissed: false
|
||||
property bool accepted: false
|
||||
}
|
||||
|
||||
Item {
|
||||
SplitView.fillHeight: true
|
||||
SplitView.fillWidth: true
|
||||
|
||||
ActivityNotificationNewDevice {
|
||||
id: notification
|
||||
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 50
|
||||
height: implicitHeight
|
||||
|
||||
type: ActivityNotificationNewDevice.InstallationType.Received
|
||||
accountName: "bob.eth"
|
||||
store: undefined
|
||||
notification: notificationMock
|
||||
|
||||
onMoreDetailsClicked: logs.logEvent("ActivityNotificationNewDevice::onMoreDetailsClicked")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
SplitView.minimumHeight: 100
|
||||
SplitView.preferredHeight: 160
|
||||
|
||||
logsView.logText: logs.logText
|
||||
|
||||
Column {
|
||||
Row {
|
||||
RadioButton {
|
||||
text: "Received"
|
||||
checked: true
|
||||
onCheckedChanged: if(checked) notification.type = ActivityNotificationNewDevice.InstallationType.Received
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: "Created"
|
||||
onCheckedChanged: if(checked) notification.type = ActivityNotificationNewDevice.InstallationType.Created
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Activity Center
|
||||
// https://www.figma.com/design/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=40765-355811&m=dev
|
|
@ -304,6 +304,8 @@ QtObject {
|
|||
|
||||
readonly property int loginType: getLoginType()
|
||||
|
||||
property string name: userProfileInst.name
|
||||
|
||||
property StickersStore stickersStore: StickersStore {
|
||||
stickersModule: stickersModuleInst
|
||||
}
|
||||
|
|
|
@ -22,13 +22,14 @@ Item {
|
|||
QtObject {
|
||||
id: d
|
||||
readonly property bool finished: startupStore.localPairingState === Constants.LocalPairingState.Finished
|
||||
readonly property bool pairingFailed: startupStore.localPairingState === Constants.LocalPairingState.Error
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
anchors.centerIn: parent
|
||||
spacing: 48
|
||||
spacing: 24
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
|
@ -55,6 +56,20 @@ Item {
|
|||
installationDeviceType: startupStore.localPairingInstallationDeviceType
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
visible: d.pairingFailed
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Use recovery phrase")
|
||||
onClicked: root.startupStore.doSecondaryAction()
|
||||
}
|
||||
|
||||
StatusFlatButton {
|
||||
visible: d.pairingFailed
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Try again")
|
||||
onClicked: root.startupStore.doTertiaryAction()
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Sign in")
|
||||
|
|
|
@ -370,6 +370,17 @@ Item {
|
|||
""
|
||||
)
|
||||
}
|
||||
|
||||
function onShowToastPairingFallbackCompleted() {
|
||||
Global.displayToastMessage(
|
||||
qsTr("Device paired"),
|
||||
qsTr("Sync in process. Keep device powered and app open."),
|
||||
"checkmark-circle",
|
||||
false,
|
||||
Constants.ephemeralNotificationType.success,
|
||||
""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import QtGraphicalEffects 1.15
|
||||
import QtQml.Models 2.15
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Backpressure 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
|
||||
import shared 1.0
|
||||
import shared.popups 1.0
|
||||
|
@ -149,6 +152,9 @@ Popup {
|
|||
return communityUnbannedNotificationComponent
|
||||
case ActivityCenterStore.ActivityCenterNotificationType.NewPrivateGroupChat:
|
||||
return groupChatInvitationNotificationComponent
|
||||
case ActivityCenterStore.ActivityCenterNotificationType.NewInstallationReceived:
|
||||
case ActivityCenterStore.ActivityCenterNotificationType.NewInstallationCreated:
|
||||
return newDeviceDetectedComponent
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
@ -316,6 +322,36 @@ Popup {
|
|||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: newDeviceDetectedComponent
|
||||
|
||||
ActivityNotificationNewDevice {
|
||||
type: setType(notification)
|
||||
|
||||
filteredIndex: parent.filteredIndex
|
||||
notification: parent.notification
|
||||
accountName: store.name
|
||||
store: root.store
|
||||
activityCenterStore: root.activityCenterStore
|
||||
onCloseActivityCenter: root.close()
|
||||
onMoreDetailsClicked: {
|
||||
switch (type) {
|
||||
case ActivityNotificationNewDevice.InstallationType.Received:
|
||||
Global.openPopup(pairDeviceDialog, {
|
||||
name: store.name,
|
||||
deviceId: notification.installationId
|
||||
});
|
||||
break;
|
||||
case ActivityNotificationNewDevice.InstallationType.Created:
|
||||
Global.openPopup(checkOtherDeviceDialog, {
|
||||
deviceId: notification.installationId
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: communityTokenReceivedComponent
|
||||
|
||||
|
@ -369,4 +405,94 @@ Popup {
|
|||
onCloseActivityCenter: root.close()
|
||||
}
|
||||
}
|
||||
|
||||
function truncateDeviceId(deviceId) {
|
||||
return deviceId.substring(0, 7).toUpperCase()
|
||||
}
|
||||
|
||||
Component {
|
||||
id: pairDeviceDialog
|
||||
|
||||
StatusDialog {
|
||||
property string name
|
||||
property string deviceId
|
||||
|
||||
width: 480
|
||||
closePolicy: Popup.CloseOnPressOutside
|
||||
destroyOnClose: true
|
||||
|
||||
title: qsTr("Pair new device and sync profile")
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 16
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("New device with %1 profile has been detected. You can see the device ID below and on your other device. Only confirm the request if the device ID matches.")
|
||||
.arg(name)
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: 27
|
||||
font.weight: Font.Medium
|
||||
font.letterSpacing: 5
|
||||
text: truncateDeviceId(deviceId)
|
||||
}
|
||||
}
|
||||
|
||||
footer: StatusDialogFooter {
|
||||
leftButtons: ObjectModel {
|
||||
StatusFlatButton {
|
||||
text: qsTr("Cancel")
|
||||
onClicked: {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
rightButtons: ObjectModel {
|
||||
StatusButton {
|
||||
text: qsTr("Pair and Sync")
|
||||
onClicked: {
|
||||
activityCenterStore.enableInstallationAndSync(deviceId)
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: checkOtherDeviceDialog
|
||||
|
||||
StatusDialog {
|
||||
property string deviceId
|
||||
|
||||
width: 480
|
||||
closePolicy: Popup.CloseOnPressOutside
|
||||
destroyOnClose: true
|
||||
|
||||
title: qsTr("Pair this device and sync profile")
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 16
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Check your other device for a pairing request. Ensure that the this device ID displayed on your other device. Only proceed with pairing and syncing if the IDs are identical.")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: 27
|
||||
font.weight: Font.Medium
|
||||
font.letterSpacing: 5
|
||||
text: truncateDeviceId(deviceId)
|
||||
}
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
footer: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,9 @@ QtObject {
|
|||
CommunityTokenReceived = 19,
|
||||
FirstCommunityTokenReceived = 20,
|
||||
CommunityBanned = 21,
|
||||
CommunityUnbanned = 22
|
||||
CommunityUnbanned = 22,
|
||||
NewInstallationReceived = 23,
|
||||
NewInstallationCreated = 24
|
||||
}
|
||||
|
||||
enum ActivityCenterReadType {
|
||||
|
@ -118,4 +120,8 @@ QtObject {
|
|||
function dismissActivityCenterNotification(notification) {
|
||||
root.activityCenterModuleInst.dismissActivityCenterNotification(notification.id)
|
||||
}
|
||||
|
||||
function enableInstallationAndSync(installationId) {
|
||||
root.activityCenterModuleInst.enableInstallationAndSync(installationId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.14
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import utils 1.0
|
||||
import mainui.activitycenter.stores 1.0
|
||||
|
||||
|
||||
ActivityNotificationBase {
|
||||
id: root
|
||||
|
||||
required property string accountName
|
||||
required property int type // Possible values [InstallationType]
|
||||
|
||||
signal moreDetailsClicked
|
||||
|
||||
function setType(notification) {
|
||||
if (notification) {
|
||||
switch (notification.notificationType) {
|
||||
case ActivityCenterStore.ActivityCenterNotificationType.NewInstallationReceived:
|
||||
return ActivityNotificationNewDevice.InstallationType.Received
|
||||
|
||||
case ActivityCenterStore.ActivityCenterNotificationType.NewInstallationCreated:
|
||||
return ActivityNotificationNewDevice.InstallationType.Created
|
||||
}
|
||||
}
|
||||
return ActivityNotificationNewDevice.InstallationType.Unknown
|
||||
}
|
||||
|
||||
enum InstallationType {
|
||||
Unknown,
|
||||
Received,
|
||||
Created
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
property string title: ""
|
||||
property string info: ""
|
||||
property string assetColor: Theme.palette.primaryColor1
|
||||
property string assetName: d.desktopAssetName
|
||||
property string assetBgColor: Theme.palette.primaryColor3
|
||||
property string ctaText: qsTr("More details")
|
||||
|
||||
readonly property string desktopAssetName: "desktop"
|
||||
}
|
||||
|
||||
bodyComponent: RowLayout {
|
||||
spacing: 8
|
||||
|
||||
StatusSmartIdenticon {
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.leftMargin: Style.current.padding
|
||||
Layout.topMargin: 2
|
||||
|
||||
asset {
|
||||
width: 24
|
||||
height: width
|
||||
name: d.assetName
|
||||
color: d.assetColor
|
||||
bgWidth: 40
|
||||
bgHeight: 40
|
||||
bgColor: d.assetBgColor
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 2
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
|
||||
StatusMessageHeader {
|
||||
Layout.fillWidth: true
|
||||
displayNameLabel.text: d.title
|
||||
timestamp: root.notification.timestamp
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.current.padding
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
text: d.info
|
||||
font.italic: true
|
||||
wrapMode: Text.WordWrap
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctaComponent: StatusFlatButton {
|
||||
size: StatusBaseButton.Size.Small
|
||||
text: d.ctaText
|
||||
onClicked: {
|
||||
root.moreDetailsClicked()
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
when: root.type === ActivityNotificationNewDevice.InstallationType.Received
|
||||
PropertyChanges {
|
||||
target: d
|
||||
title: qsTr("New device detected")
|
||||
info: qsTr("New device with %1 profile has been detected.").arg(accountName)
|
||||
}
|
||||
},
|
||||
State {
|
||||
when: root.type === ActivityNotificationNewDevice.InstallationType.Created
|
||||
PropertyChanges {
|
||||
target: d
|
||||
title: qsTr("Sync your profile")
|
||||
info: qsTr("Check your other device for a pairing request.")
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -2,3 +2,4 @@ ActivityNotificationCommunityMembershipRequest 1.0 ActivityNotificationCommunity
|
|||
ActivityNotificationTransferOwnership 1.0 ActivityNotificationTransferOwnership.qml
|
||||
ActivityNotificationCommunityShareAddresses 1.0 ActivityNotificationCommunityShareAddresses.qml
|
||||
ActivityNotificationCommunityTokenReceived 1.0 ActivityNotificationCommunityTokenReceived.qml
|
||||
ActivityNotificationNewDevice 1.0 ActivityNotificationNewDevice.qml
|
||||
|
|
|
@ -27,6 +27,18 @@ Rectangle {
|
|||
property bool detailsVisible: false
|
||||
}
|
||||
|
||||
CopyButton {
|
||||
width: 20
|
||||
height: 20
|
||||
visible: d.detailsVisible
|
||||
color: Theme.palette.baseColor1
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 8
|
||||
anchors.rightMargin: 8
|
||||
textToCopy: root.details
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
|
||||
|
|
Loading…
Reference in New Issue