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:
parent
4e3263fddd
commit
0689a8f386
|
@ -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) =
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -312,4 +312,8 @@ QtObject {
|
|||
function getMonthYear(value) {
|
||||
return formatDate(value, "MMM yyyy")
|
||||
}
|
||||
|
||||
function getDayName(value) {
|
||||
return Qt.locale().standaloneDayName(value.getDay())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue