fix(Syncing): Fixed synced devices list UI bugs (#9929)

* Added online badge to device delegates
* Fixing device last timestamp processing
* Synced devices sorting - current device is always first
* Devices list scrolling fixes
* Fix device list duplication
* Hide online badge for "Synced device" subtitle
This commit is contained in:
Igor Sirotin 2023-03-21 16:10:06 +11:00 committed by GitHub
parent 4e3263fddd
commit 0689a8f386
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 143 additions and 112 deletions

View File

@ -80,18 +80,10 @@ QtObject:
of ModelRole.IsCurrentDevice:
result = newQVariant(item.isCurrentDevice)
proc addItems*(self: Model, items: seq[Item]) =
if(items.len == 0):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
let first = self.items.len
let last = first + items.len - 1
self.beginInsertRows(parentModelIndex, first, last)
self.items.add(items)
self.endInsertRows()
proc setItems*(self: Model, items: seq[Item]) =
self.beginResetModel()
self.items = items
self.endResetModel()
self.countChanged()
proc addItem*(self: Model, item: Item) =

View File

@ -64,7 +64,7 @@ method onDevicesLoaded*(self: Module, allDevices: seq[InstallationDto]) =
for d in allDevices:
let item = initItem(d, self.isMyDevice(d.id))
items.add(item)
self.view.model().addItems(items)
self.view.model().setItems(items)
self.view.setDevicesLoading(false)
self.view.deviceSetupChanged()

View File

@ -545,34 +545,34 @@ ColumnLayout {
StatusBaseText {
Layout.fillWidth: true
Layout.topMargin: 16
text: "Loading features:"
text: "Device delegate with online badge"
font.pixelSize: 17
}
StatusListItem {
component DeviceListItem: StatusListItem {
title: "Nokia 3310"
subTitle: "Incoming device"
label: "loading: true"
asset.width: 40
asset.height: 40
asset.emoji: "😁"
asset.color: "hotpink"
asset.letterSize: 14
asset.isLetterIdenticon: true
loading: true
asset.name: "mobile"
asset.bgColor: Theme.palette.primaryColor3
asset.color: Theme.palette.primaryColor1
}
StatusListItem {
title: "Nokia 3310"
subTitle: "Device"
label: "loadingFailed: true"
asset.width: 40
asset.height: 40
asset.emoji: "😁"
asset.color: "hotpink"
asset.letterSize: 14
asset.isLetterIdenticon: true
loadingFailed: true
DeviceListItem {
subTitle: "Online now"
subTitleBadgeComponent: StatusOnlineBadge {
online: true
}
}
DeviceListItem {
subTitle: "Online 47 minutes ago"
subTitleBadgeComponent: StatusOnlineBadge {
online: false
}
}
DeviceListItem {
subTitle: "This device"
subTitleBadgeComponent: null
}
StatusListItem {

View File

@ -1,4 +1,4 @@
import QtQuick 2.14
import QtQuick 2.15
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
@ -26,7 +26,7 @@ Column {
width: 200
}
StatusListItem {
component DeviceListItem: StatusListItem {
title: "Nokia 3310"
subTitle: "Incoming device"
asset.width: 40
@ -35,34 +35,20 @@ Column {
asset.color: "hotpink"
asset.letterSize: 14
asset.isLetterIdenticon: true
}
DeviceListItem {
statusListItemSubTitle.loading: loadingButton.checked
}
StatusListItem {
title: "Nokia 3310"
subTitle: "Incoming device"
asset.width: 40
asset.height: 40
asset.emoji: "😁"
asset.color: "hotpink"
asset.letterSize: 14
asset.isLetterIdenticon: true
DeviceListItem {
statusListItemSubTitle.loading: loadingButton.checked
statusListItemIcon.loading: loadingButton.checked
}
StatusListItem {
title: "Nokia 3310"
subTitle: "Incoming device"
asset.width: 40
asset.height: 40
asset.emoji: "😁"
asset.color: "hotpink"
asset.letterSize: 14
asset.isLetterIdenticon: true
DeviceListItem {
statusListItemTitle.loading: loadingButton.checked
statusListItemSubTitle.loading: loadingButton.checked
statusListItemIcon.loading: loadingButton.checked
}
}

View File

@ -33,7 +33,6 @@ Rectangle {
property var inlineTagModel: []
property Component inlineTagDelegate
property bool loading: false
property bool loadingFailed: false
property StatusAssetSettings asset: StatusAssetSettings {
height: isImage ? 40 : 20
@ -78,6 +77,7 @@ Rectangle {
property alias statusListItemTagsSlot: statusListItemTagsSlot
property alias statusListItemInlineTagsSlot: statusListItemTagsSlotInline
property alias statusListItemLabel: statusListItemLabel
property alias subTitleBadgeComponent: subTitleBadgeLoader.sourceComponent
signal clicked(string itemId, var mouse)
signal titleClicked(string titleId)
@ -257,6 +257,12 @@ Rectangle {
width: parent.width
spacing: 4
Loader {
id: subTitleBadgeLoader
Layout.alignment: Qt.AlignVCenter
visible: sourceComponent
}
StatusTextWithLoadingState {
id: statusListItemSubTitle
objectName: "statusListItemSubTitle"

View File

@ -0,0 +1,15 @@
import QtQuick 2.15
import StatusQ.Core.Theme 0.1
StatusBadge {
id: root
property bool online: false
implicitHeight: 11
implicitWidth: 11
color: online ? Theme.palette.successColor1 : Theme.palette.baseColor1
border.width: 2
border.color: Theme.palette.baseColor4
radius: Math.ceil(width / 2)
}

View File

@ -15,8 +15,9 @@ StatusListItem {
property string deviceName: ""
property string deviceType: ""
property real timestamp: 0
property bool isCurrentDevice: false
property int timestamp: 0
property bool showOnlineBadge: !isCurrentDevice
signal itemClicked
signal setupSyncingButtonClicked
@ -30,35 +31,29 @@ StatusListItem {
subTitle: {
if (root.isCurrentDevice)
return qsTr("This device");
return qsTr("This device")
if (d.secondsFromSync <= 120)
return qsTr("Online now");
if (d.onlineNow)
return qsTr("Online now")
if (d.minutesFromSync <= 60)
return qsTr("Online %n minutes(s) ago", "", d.minutesFromSync);
return qsTr("Online %n minute(s) ago", "", d.minutesFromSync)
if (d.daysFromSync == 0)
return qsTr("Last seen earlier today");
return qsTr("Last seen earlier today")
if (d.daysFromSync == 1)
return qsTr("Last online yesterday");
return qsTr("Last online yesterday")
const date = new Date(d.deviceLastTimestamp)
if (d.daysFromSync <= 6)
return qsTr("Last online [%1]").arg(Qt.locale().dayName[d.lastSyncDate.getDay()]);
return qsTr("Last online [%1]").arg(LocaleUtils.getDayName(date))
return qsTr("Last online %1").arg(LocaleUtils.formatDate(lastSyncDate))
return qsTr("Last online %1").arg(LocaleUtils.formatDate(date))
}
QtObject {
id: d
readonly property var lastSyncDate: new Date(root.timestamp)
readonly property int millisecondsFromSync: lastSyncDate - Date.now()
readonly property int secondsFromSync: millisecondsFromSync / 1000
readonly property int minutesFromSync: secondsFromSync / 60
readonly property int daysFromSync: new Date().getDay() - lastSyncDate.getDay()
}
subTitleBadgeComponent: root.showOnlineBadge ? onlineBadgeComponent : null
components: [
StatusButton {
@ -78,4 +73,34 @@ StatusListItem {
color: Theme.palette.baseColor1
}
]
QtObject {
id: d
property real now: 0
readonly property int deviceLastTimestamp: root.timestamp / 1000000
readonly property int secondsFromSync: (now - Math.max(0, d.deviceLastTimestamp)) / 1000
readonly property int minutesFromSync: secondsFromSync / 60
readonly property int hoursFromSync: minutesFromSync / 60
readonly property int daysFromSync: hoursFromSync / 24
readonly property bool onlineNow: secondsFromSync <= 120
}
Timer {
interval: 1000
repeat: true
triggeredOnStart: true
running: root.showOnlineBadge && root.visible
onTriggered: {
d.now = Date.now()
}
}
Component {
id: onlineBadgeComponent
StatusOnlineBadge {
online: d.onlineNow
}
}
}

View File

@ -52,3 +52,4 @@ StatusStepper 0.1 StatusStepper.qml
LoadingComponent 0.1 LoadingComponent.qml
StatusQrCodeScanner 0.1 StatusQrCodeScanner.qml
StatusSyncDeviceDelegate 0.1 StatusSyncDeviceDelegate.qml
StatusOnlineBadge 0.1 StatusOnlineBadge.qml

View File

@ -312,4 +312,8 @@ QtObject {
function getMonthYear(value) {
return formatDate(value, "MMM yyyy")
}
function getDayName(value) {
return Qt.locale().standaloneDayName(value.getDay())
}
}

View File

@ -197,5 +197,6 @@
<file>StatusQ/Core/Utils/ModelsComparator.qml</file>
<file>StatusQ/Core/Utils/ModelChangeTracker.qml</file>
<file>StatusQ/Components/StatusQrCodeScanner.qml</file>
<file>StatusQ/Components/StatusOnlineBadge.qml</file>
</qresource>
</RCC>

View File

@ -14,7 +14,7 @@ Item {
property string sectionTitle
property int contentWidth
readonly property int contentHeight: (root.height - d.topHeaderHeight - d.titleRowHeight)
readonly property int contentHeight: root.height - titleRow.height - Style.current.padding
property alias titleRowComponentLoader: loader
property list<Item> headerComponents
@ -35,7 +35,6 @@ Item {
QtObject {
id: d
readonly property int topHeaderHeight: 56
readonly property int titleRowHeight: 56
}
@ -58,6 +57,7 @@ Item {
id: titleRow
width: root.contentWidth
spacing: 0
RowLayout {
Layout.preferredWidth: (parent.width - Style.current.padding)
Layout.preferredHeight: visible ? d.titleRowHeight : 0

View File

@ -17,6 +17,8 @@ import shared.panels 1.0
import shared.controls 1.0
import shared.controls.chat 1.0
import SortFilterProxyModel 0.2
import "../stores"
import "../popups"
import "../controls"
@ -29,37 +31,23 @@ SettingsContentBase {
property ProfileStore profileStore
property PrivacyStore privacyStore
property bool isSyncing: false
Component.onCompleted: {
root.devicesStore.loadDevices()
}
ColumnLayout {
id: layout
width: root.contentWidth
spacing: Style.current.padding
QtObject {
id: d
/*
Device INFO:
id: "abcdabcd-1234-5678-9012-12a34b5cd678",
identity: ""
version: 1
enabled: true
timestamp: 0
metadata:
name: "MacBook-1"
deviceType: "macosx"
fcmToken: ""
*/
readonly property var instructionsModel: [
qsTr("Verify your login with password or KeyCard"),
qsTr("Reveal a temporary QR and Sync Code") + "*",
qsTr("Share that information with your new device"),
]
qsTr("Verify your login with password or KeyCard"),
qsTr("Reveal a temporary QR and Sync Code") + "*",
qsTr("Share that information with your new device"),
]
function personalizeDevice(model) {
@ -94,36 +82,45 @@ SettingsContentBase {
StatusBaseText {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
text: qsTr("Devices")
font.pixelSize: 15
font.pixelSize: Constants.settingsSection.subHeaderFontSize
color: Theme.palette.baseColor1
}
StatusBaseText {
Layout.fillWidth: true
visible: root.devicesStore.devicesModule.devicesLoading
horizontalAlignment: Text.AlignHCenter
text: qsTr("Loading devices...")
}
StatusBaseText {
Layout.fillWidth: true
visible: root.devicesStore.devicesModule.devicesLoadingError
horizontalAlignment: Text.AlignHCenter
text: qsTr("Error loading devices. Please try again later.")
color: Theme.palette.dangerColor1
}
ListView {
id: listView
StatusListView {
Layout.fillWidth: true
Layout.topMargin: 17
Layout.bottomMargin: 17
implicitHeight: contentHeight
spacing: Style.current.padding
model: root.devicesStore.devicesModel
interactive: false
spacing: 0
visible: !root.devicesStore.devicesModule.devicesLoading &&
!root.devicesStore.devicesModule.devicesLoadingError &&
root.devicesStore.isDeviceSetup
!root.devicesStore.devicesModule.devicesLoadingError &&
root.devicesStore.isDeviceSetup
model: SortFilterProxyModel {
sourceModel: root.devicesStore.devicesModel
sorters: StringSorter {
roleName: "isCurrentDevice"
sortOrder: Qt.DescendingOrder
}
}
delegate: StatusSyncDeviceDelegate {
width: ListView.view.width
@ -218,7 +215,6 @@ SettingsContentBase {
}
StatusButton {
// type: StatusRoundButton.Type.Secondary
Layout.alignment: Qt.AlignHCenter
normalColor: Theme.palette.primaryColor1
hoverColor: Theme.palette.miscColor1;
@ -243,10 +239,9 @@ SettingsContentBase {
StatusButton {
id: backupBtn
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 17
text: qsTr("Backup Data")
onClicked : {
let lastUpdate = root.privacyStore.backupData() * 1000
const lastUpdate = root.privacyStore.backupData() * 1000
console.log("Backup done at: ", LocaleUtils.formatDateTime(lastUpdate))
}
}
@ -269,5 +264,10 @@ SettingsContentBase {
profileStore: root.profileStore
}
}
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
}
}

View File

@ -130,6 +130,7 @@ Item {
loading: d.pairingInProgress
deviceName: qsTr("No device name")
isCurrentDevice: false
showOnlineBadge: false
}
ErrorDetails {
@ -166,14 +167,14 @@ Item {
implicitWidth: contentWidth + leftPadding + rightPadding
implicitHeight: contentHeight + topPadding + bottomPadding
ListView {
StatusListView {
id: listView
width: scrollView.availableWidth
height: scrollView.availableHeight
spacing: 4
clip: true
delegate: StatusSyncDeviceDelegate {
width: ListView.view.width
enabled: false