diff --git a/src/app/modules/main/profile_section/devices/model.nim b/src/app/modules/main/profile_section/devices/model.nim
index 0f8c9e5f0d..e99177c602 100644
--- a/src/app/modules/main/profile_section/devices/model.nim
+++ b/src/app/modules/main/profile_section/devices/model.nim
@@ -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) =
diff --git a/src/app/modules/main/profile_section/devices/module.nim b/src/app/modules/main/profile_section/devices/module.nim
index 1ba10e76be..03e0fc29e2 100644
--- a/src/app/modules/main/profile_section/devices/module.nim
+++ b/src/app/modules/main/profile_section/devices/module.nim
@@ -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()
diff --git a/ui/StatusQ/sandbox/controls/ListItems.qml b/ui/StatusQ/sandbox/controls/ListItems.qml
index 2f0d81ea24..d38f735b69 100644
--- a/ui/StatusQ/sandbox/controls/ListItems.qml
+++ b/ui/StatusQ/sandbox/controls/ListItems.qml
@@ -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 {
diff --git a/ui/StatusQ/sandbox/controls/LoadingStates.qml b/ui/StatusQ/sandbox/controls/LoadingStates.qml
index c1a90aca4f..a72c5e0918 100644
--- a/ui/StatusQ/sandbox/controls/LoadingStates.qml
+++ b/ui/StatusQ/sandbox/controls/LoadingStates.qml
@@ -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
}
-
}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml b/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml
index 5f5d90453e..55af5a6683 100644
--- a/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml
+++ b/ui/StatusQ/src/StatusQ/Components/StatusListItem.qml
@@ -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"
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusOnlineBadge.qml b/ui/StatusQ/src/StatusQ/Components/StatusOnlineBadge.qml
new file mode 100644
index 0000000000..b749dc2aa5
--- /dev/null
+++ b/ui/StatusQ/src/StatusQ/Components/StatusOnlineBadge.qml
@@ -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)
+}
diff --git a/ui/StatusQ/src/StatusQ/Components/StatusSyncDeviceDelegate.qml b/ui/StatusQ/src/StatusQ/Components/StatusSyncDeviceDelegate.qml
index de4e709111..ff2c3b3291 100644
--- a/ui/StatusQ/src/StatusQ/Components/StatusSyncDeviceDelegate.qml
+++ b/ui/StatusQ/src/StatusQ/Components/StatusSyncDeviceDelegate.qml
@@ -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
+ }
+ }
}
diff --git a/ui/StatusQ/src/StatusQ/Components/qmldir b/ui/StatusQ/src/StatusQ/Components/qmldir
index 9579a5f278..dd7bde4475 100644
--- a/ui/StatusQ/src/StatusQ/Components/qmldir
+++ b/ui/StatusQ/src/StatusQ/Components/qmldir
@@ -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
diff --git a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml
index 6cd3bedda0..e0891869ce 100644
--- a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml
+++ b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml
@@ -312,4 +312,8 @@ QtObject {
function getMonthYear(value) {
return formatDate(value, "MMM yyyy")
}
+
+ function getDayName(value) {
+ return Qt.locale().standaloneDayName(value.getDay())
+ }
}
diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc
index e32805d048..ca2e9bb8b5 100644
--- a/ui/StatusQ/src/statusq.qrc
+++ b/ui/StatusQ/src/statusq.qrc
@@ -197,5 +197,6 @@
StatusQ/Core/Utils/ModelsComparator.qml
StatusQ/Core/Utils/ModelChangeTracker.qml
StatusQ/Components/StatusQrCodeScanner.qml
+ StatusQ/Components/StatusOnlineBadge.qml
diff --git a/ui/app/AppLayouts/Profile/views/SettingsContentBase.qml b/ui/app/AppLayouts/Profile/views/SettingsContentBase.qml
index 24d06fcd85..84b054b93a 100644
--- a/ui/app/AppLayouts/Profile/views/SettingsContentBase.qml
+++ b/ui/app/AppLayouts/Profile/views/SettingsContentBase.qml
@@ -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- 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
diff --git a/ui/app/AppLayouts/Profile/views/SyncingView.qml b/ui/app/AppLayouts/Profile/views/SyncingView.qml
index 37f755e019..91bcdf7593 100644
--- a/ui/app/AppLayouts/Profile/views/SyncingView.qml
+++ b/ui/app/AppLayouts/Profile/views/SyncingView.qml
@@ -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
+ }
}
}
diff --git a/ui/imports/shared/views/DeviceSyncingView.qml b/ui/imports/shared/views/DeviceSyncingView.qml
index d1cd2f5828..aeb6984e07 100644
--- a/ui/imports/shared/views/DeviceSyncingView.qml
+++ b/ui/imports/shared/views/DeviceSyncingView.qml
@@ -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