chore: Consolidate date/time formatting

- simplify locale settings
- extract/fix datetime related functions into LocaleUtils
- port code to the new LocaleUtils

Closes #7230
This commit is contained in:
Lukáš Tinkl 2023-01-12 23:39:46 +01:00 committed by Lukáš Tinkl
parent b981f31591
commit b2328d6643
39 changed files with 272 additions and 439 deletions

View File

@ -89,10 +89,6 @@ const LSS_KEY_COMPATIBILITY_MODE* = "compatibilityMode"
const DEFAULT_COMPATIBILITY_MODE = true
const LSS_KEY_STICKERS_ENS_ROPSTEN* = "stickersEnsRopsten"
const DEFAULT_STICKERS_ENS_ROPSTEN = false
const LSS_KEY_IS_DDMMYY_DATE_FORMAT* = "is_DDMMYY_date_format"
const DEFAULT_IS_DDMMYY_DATE_FORMAT = false
const LSS_KEY_IS_24H_TIME_FORMAT* = "is_24h_time_format"
const DEFAULT_IS_24H_TIME_FORMAT = false
const LSS_KEY_USER_DECLINED_BACKUP_BANNER* = "userDeclinedBackupBanner"
const DEFAULT_USER_DECLINED_BACKUP_BANNER = false
const LSS_KEY_IS_DISCORD_IMPORT_TOOL_ENABLED* = "isDiscordImportToolEnabled"
@ -756,31 +752,6 @@ QtObject:
write = setStickersEnsRopsten
notify = stickersEnsRopstenChanged
proc isDDMMYYDateFormatChanged*(self: LocalAccountSensitiveSettings) {.signal.}
proc getIsDDMMYYDateFormat*(self: LocalAccountSensitiveSettings): bool {.slot.} =
getSettingsProp[bool](self, LSS_KEY_IS_DDMMYY_DATE_FORMAT, newQVariant(DEFAULT_IS_DDMMYY_DATE_FORMAT))
proc setIsDDMMYYDateFormat*(self: LocalAccountSensitiveSettings, value: bool) {.slot.} =
setSettingsProp(self, LSS_KEY_IS_DDMMYY_DATE_FORMAT, newQVariant(value)):
self.isDDMMYYDateFormatChanged()
QtProperty[bool] isDDMMYYDateFormat:
read = getIsDDMMYYDateFormat
write = setIsDDMMYYDateFormat
notify = isDDMMYYDateFormatChanged
proc is24hTimeFormatChanged*(self: LocalAccountSensitiveSettings) {.signal.}
proc getIs24hTimeFormat*(self: LocalAccountSensitiveSettings): bool {.slot.} =
getSettingsProp[bool](self, LSS_KEY_IS_24H_TIME_FORMAT, newQVariant(DEFAULT_IS_24H_TIME_FORMAT))
proc setIs24hTimeFormat*(self: LocalAccountSensitiveSettings, value: bool) {.slot.} =
setSettingsProp(self, LSS_KEY_IS_24H_TIME_FORMAT, newQVariant(value)):
self.is24hTimeFormatChanged()
QtProperty[bool] is24hTimeFormat:
read = getIs24hTimeFormat
write = setIs24hTimeFormat
notify = is24hTimeFormatChanged
proc userDeclinedBackupBannerChanged*(self: LocalAccountSensitiveSettings) {.signal.}
proc getUserDeclinedBackupBanner*(self: LocalAccountSensitiveSettings): bool {.slot.} =
getSettingsProp[bool](self, LSS_KEY_USER_DECLINED_BACKUP_BANNER, newQVariant(DEFAULT_USER_DECLINED_BACKUP_BANNER))
@ -843,6 +814,4 @@ QtObject:
of LSS_KEY_PDF_VIEWER_ENABLED: self.pdfViewerEnabledChanged()
of LSS_KEY_COMPATIBILITY_MODE: self.compatibilityModeChanged()
of LSS_KEY_STICKERS_ENS_ROPSTEN: self.stickersEnsRopstenChanged()
of LSS_KEY_IS_DDMMYY_DATE_FORMAT: self.isDDMMYYDateFormatChanged()
of LSS_KEY_IS_24H_TIME_FORMAT: self.is24hTimeFormatChanged()
of LSS_KEY_USER_DECLINED_BACKUP_BANNER: self.userDeclinedBackupBannerChanged()

View File

@ -19,12 +19,6 @@ method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
method changeLanguage*(self: AccessInterface, language: string) {.base.} =
raise newException(ValueError, "No implementation available")
method setIsDDMMYYDateFormat*(self: AccessInterface, isDDMMYYDateFormat: bool) {.slot.} =
raise newException(ValueError, "No implementation available")
method setIs24hTimeFormat*(self: AccessInterface, is24hTimeFormat: bool) {.slot.} =
raise newException(ValueError, "No implementation available")
method onCurrentLanguageChanged*(self: AccessInterface, language: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -70,12 +70,4 @@ method changeLanguage*(self: Module, language: string) =
method onCurrentLanguageChanged*(self: Module, language: string) =
self.view.setLanguage(language)
method setIsDDMMYYDateFormat*(self: Module, isDDMMYYDateFormat: bool) =
if(isDDMMYYDateFormat != singletonInstance.localAccountSensitiveSettings.getIsDDMMYYDateFormat()):
singletonInstance.localAccountSensitiveSettings.setIsDDMMYYDateFormat(isDDMMYYDateFormat)
method setIs24hTimeFormat*(self: Module, is24hTimeFormat: bool) =
if(is24hTimeFormat != singletonInstance.localAccountSensitiveSettings.getIs24hTimeFormat()):
singletonInstance.localAccountSensitiveSettings.setIs24hTimeFormat(is24hTimeFormat)

View File

@ -34,12 +34,6 @@ QtObject:
QtProperty[QVariant] model:
read = getModel
proc setIsDDMMYYDateFormat*(self: View, isDDMMYYDateFormat: bool) {.slot.} =
self.delegate.setIsDDMMYYDateFormat(isDDMMYYDateFormat)
proc setIs24hTimeFormat*(self: View, is24hTimeFormat: bool) {.slot.} =
self.delegate.setIs24hTimeFormat(is24hTimeFormat)
proc changeLanguage*(self: View, language: string) {.slot.} =
self.delegate.changeLanguage(language)

View File

@ -23,8 +23,6 @@ SplitView {
languageStore: LanguageStore {
property string currentLanguage: "en"
readonly property bool isDDMMYYDateFormat: true
readonly property bool is24hTimeFormat: true
readonly property ListModel languageModel: ListModel {
ListElement {
@ -49,14 +47,6 @@ SplitView {
logs.logEvent("languageStore::changeLanguage", ["language"], arguments)
currentLanguage = language
}
function setIsDDMMYYDateFormat(isDDMMYYDateFormat) {
logs.logEvent("languageStore::setIsDDMMYYDateFormat", ["isDDMMYYDateFormat"], arguments)
}
function setIs24hTimeFormat(is24hTimeFormat) {
logs.logEvent("languageStore::setIs24hTimeFormat", ["is24hTimeFormat"], arguments)
}
}
currencyStore: QtObject {

View File

@ -27,10 +27,9 @@ StatusComboBox {
\qmlproperty string StatusDatePicker::dateFormat
This property specifies how the selected date will be displayed to the user.
By default it is current locale's short date format. For a list of available types
and formatting characters, see https://doc.qt.io/qt-5/qdate.html#toString-2
Either Locale.ShortFormat (default) or Locale.LongFormat
*/
property string dateFormat: Qt.locale().dateFormat(Locale.ShortFormat)
property int dateFormat: Locale.ShortFormat
/*!
\qmlproperty date StatusDatePicker::selectedDate
@ -46,7 +45,7 @@ StatusComboBox {
}
control.delegate: null
control.displayText: root.selectedDate.toLocaleDateString(Qt.locale(), root.dateFormat)
control.displayText: LocaleUtils.formatDate(root.selectedDate, root.dateFormat)
control.popup.horizontalPadding: 8
control.popup.onAboutToShow: {

View File

@ -63,8 +63,8 @@ Control {
property StatusMessageDetails messageDetails: StatusMessageDetails {}
property StatusMessageDetails replyDetails: StatusMessageDetails {}
property string timestampString: new Date(timestamp).toLocaleTimeString(Qt.locale(), Locale.ShortFormat)
property string timestampTooltipString: new Date(timestamp).toLocaleString()
property string timestampString: LocaleUtils.formatTime(timestamp, Locale.ShortFormat)
property string timestampTooltipString: LocaleUtils.formatDateTime(timestamp)
signal clicked(var sender, var mouse)
signal profilePictureClicked(var sender, var mouse)

View File

@ -23,6 +23,8 @@ CheckBox {
property bool leftSide: true
opacity: enabled ? 1.0 : 0.3
QtObject {
id: d
@ -66,7 +68,6 @@ CheckBox {
contentItem: StatusBaseText {
text: root.text
font: root.font
opacity: enabled ? 1.0 : 0.3
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
width: parent.width

View File

@ -0,0 +1,196 @@
pragma Singleton
import QtQml 2.14
import Qt.labs.settings 1.0
QtObject {
id: root
function fractionalPartLength(num) {
if (Number.isInteger(num))
return 0
return num.toString().split('.')[1].length
}
function stripTrailingZeroes(numStr, locale) {
let regEx = locale.decimalPoint == "." ? /(\.[0-9]*[1-9])0+$|\.0*$/ : /(\,[0-9]*[1-9])0+$|\,0*$/
return numStr.replace(regEx, '$1')
}
function numberToLocaleString(num, precision = -1, locale = null) {
locale = locale || Qt.locale()
if (precision === -1)
precision = fractionalPartLength(num)
return num.toLocaleString(locale, 'f', precision)
}
function currencyAmountToLocaleString(currencyAmount, locale) {
if (!locale) {
console.log("Unspecified locale for: " + JSON.stringify(currencyAmount))
locale = Qt.locale()
}
if (typeof(currencyAmount) !== "object") {
console.log("Wrong type for currencyAmount: " + JSON.stringify(currencyAmount))
return NaN
}
var amountStr = numberToLocaleString(currencyAmount.amount, currencyAmount.displayDecimals, locale)
if (currencyAmount.stripTrailingZeroes) {
amountStr = stripTrailingZeroes(amountStr, locale)
}
if (currencyAmount.symbol) {
amountStr = "%1 %2".arg(amountStr).arg(currencyAmount.symbol)
}
return amountStr
}
// DATE/TIME
readonly property var d: QtObject {
id: d
readonly property var amPmFormatChars: ["AP", "A", "ap", "a"]
// try to parse date from a number or ISO string timestamp
function readDate(value) {
if (typeof value === "undefined") // default to "now" if omitted
return new Date()
if (typeof value === "string" || typeof value === "number") // support reading ISO string or numeric timestamps
return new Date(value)
return value
}
// enforce correct 12/24 time format when not using defaults
function fixupTimeFormatString(formatString) {
if (settings.timeFormatUsesDefaults) // OS defaults, nothing to change
return formatString
if (settings.timeFormatUses24Hours) { // enforce 24h time format
// remove any amPmFormatChars from the format string and use 0-23 hours (h->H, hh->HH)
return formatString.replace(/\ba\b|\bap\b/ig, "").replace(/h/g, "H")
} else { // enforce 12hr time format
// use 0-12 hours (H->h, HH->h) and append any of amPmFormatChars to the format string
var result = formatString.replace(/H/g, "h")
if (!d.amPmFormatChars.some(ampm => result.includes(ampm)))
result = result.concat(" ap")
return result
}
}
}
readonly property Settings settings: Settings {
category: "Locale"
property bool timeFormatUsesDefaults: true
property bool timeFormatUses24Hours: is24hTimeFormatDefault()
}
function is24hTimeFormatDefault() {
const timeFormatString = Qt.locale().timeFormat(Locale.LongFormat)
return !d.amPmFormatChars.some(ampm => timeFormatString.includes(ampm))
}
/**
Converts the Date to a string containing the date suitable for the specified locale in the specified format.
- 'value' can be either a Date object, or a string containing a number or ISO string timestamp, or empty for "now"
- 'format' can be one of Locale.LongFormat (default), Locale.ShortFormat
*/
function formatDate(value, format = Locale.LongFormat) {
value = d.readDate(value)
const loc = Qt.locale()
if (format === Locale.ShortFormat) { // replace 2-digit year with 4-digits (yy -> yyyy) in short format
const dateFormatStr = loc.dateFormat(Locale.ShortFormat)
if (!dateFormatStr.includes("yyyy")) {
format = dateFormatStr.replace("yy", "yyyy")
}
}
return value.toLocaleDateString(loc, format)
}
/**
Converts the Date to a string containing the time suitable for the specified locale in the specified format.
- 'value' can be either a Date object, or a string containing a number or ISO string timestamp, or empty for "now"
- 'format' can be one of Locale.LongFormat (default), Locale.ShortFormat
*/
function formatTime(value, format = Locale.LongFormat) {
value = d.readDate(value)
const loc = Qt.locale()
const formatString = d.fixupTimeFormatString(loc.timeFormat(format))
return value.toLocaleTimeString(loc, formatString)
}
/**
Converts the Date to a string containing both the date and time suitable for the specified locale in the specified format.
- 'value' can be either a Date object, or a string containing a number or ISO string timestamp, or empty for "now"
- 'format' can be one of Locale.LongFormat (default), Locale.ShortFormat
*/
function formatDateTime(value, format = Locale.LongFormat) {
value = d.readDate(value)
const loc = Qt.locale()
var formatString = d.fixupTimeFormatString(loc.dateTimeFormat(format))
if (format === Locale.ShortFormat && !formatString.includes("yyyy")) // replace 2-digit year with 4-digits (yy -> yyyy) in short format
formatString = formatString.replace("yy", "yyyy")
return value.toLocaleString(loc, formatString)
}
function getTimeDifference(d1, d2) {
const day1Year = d1.getFullYear()
const day1Month = d1.getMonth()
const day1Time = d1.getTime()
const day2Year = d2.getFullYear()
const day2Month = d2.getMonth()
const day2Time = d2.getTime()
const inYears = day2Year-day1Year
if (inYears > 0) {
return qsTr("%n year(s) ago", "", inYears)
}
const inMonths = (day2Month+12*day2Year)-(day1Month+12*day1Year)
if (inMonths > 0) {
return qsTr("%n month(s) ago", "", inMonths)
}
const inWeeks = parseInt((day2Time-day2Time)/(24*3600*1000*7))
if (inWeeks > 0) {
return qsTr("%n week(s) ago", "", inWeeks)
}
const inDays = parseInt((day2Time-day1Time)/(24*3600*1000))
if (inDays > 0) {
return qsTr("%n day(s) ago", "", inDays)
}
const inHours = parseInt((day2Time-day1Time)/(3600*1000));
if (inHours > 0) {
return qsTr("%n hour(s) ago", "", inHours)
}
const inMins = parseInt((day2Time-day1Time)/(60*1000))
if (inMins > 0) {
return qsTr("%n min(s) ago", "x minute(s) ago", inMins)
}
const inSecs = parseInt((day2Time-day1Time)/1000);
if (inSecs > 0) {
return qsTr("%n sec(s) ago", "x second(s) ago", inSecs)
}
return qsTr("now")
}
// FIXME Qt6 use IntlFormat (partial string)
function getDayMonth(value) {
const currentFormat = is24hTimeFormatDefault() ? "d MMM" : "MMM d"
return formatDate(value, currentFormat)
}
// FIXME Qt6 use IntlFormat (partial string)
function getMonthYear(value) {
return formatDate(value, "MMM yyyy")
}
}

View File

@ -14,3 +14,4 @@ StatusProfileImageSettings 0.1 StatusProfileImageSettings.qml
StatusRollArea 0.1 StatusRollArea.qml
StatusScrollView 0.1 StatusScrollView.qml
StatusTooltipSettings 0.1 StatusTooltipSettings.qml
singleton LocaleUtils 0.1 LocaleUtils.qml

View File

@ -128,6 +128,7 @@
<file>StatusQ/Core/Utils/qmldir</file>
<file>StatusQ/Core/Utils/Utils.qml</file>
<file>StatusQ/Core/qmldir</file>
<file>StatusQ/Core/LocaleUtils.qml</file>
<file>StatusQ/Core/StatusAnimatedStack.qml</file>
<file>StatusQ/Core/StatusBaseText.qml</file>
<file>StatusQ/Core/StatusFontSettings.qml</file>

View File

@ -7,6 +7,7 @@ import Qt.labs.settings 1.0
import QtQuick.Controls.Styles 1.0
import QtQuick.Dialogs 1.2
import StatusQ.Core 0.1
import StatusQ.Layout 0.1
import utils 1.0

View File

@ -1,6 +1,7 @@
import QtQuick 2.0
import AppLayouts.Chat.controls.community 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1 as SQ
import utils 1.0

View File

@ -28,7 +28,7 @@ QtObject {
property bool discordImportHasCommunityImage: root.communitiesModuleInst.discordImportHasCommunityImage
property var discordImportTasks: root.communitiesModuleInst.discordImportTasks
property bool downloadingCommunityHistoryArchives: root.communitiesModuleInst.downloadingCommunityHistoryArchives
property var locale: Qt.locale(localAppSettings.language)
property var locale: Qt.locale()
property var advancedModule: profileSectionModule.advancedModule
// TODO: Could the backend provide directly 2 filtered models??

View File

@ -8,18 +8,8 @@ QtObject {
readonly property var languageModel: languageModule ? languageModule.model : null
readonly property string currentLanguage: languageModule ? languageModule.currentLanguage : null
readonly property bool isDDMMYYDateFormat: localAccountSensitiveSettings.isDDMMYYDateFormat
readonly property bool is24hTimeFormat: localAccountSensitiveSettings.is24hTimeFormat
function changeLanguage(language) {
root.languageModule.changeLanguage(language)
}
function setIsDDMMYYDateFormat(isDDMMYYDateFormat) {
root.languageModule.setIsDDMMYYDateFormat(isDDMMYYDateFormat)
}
function setIs24hTimeFormat(is24hTimeFormat) {
root.languageModule.setIs24hTimeFormat(is24hTimeFormat)
}
}

View File

@ -218,7 +218,7 @@ Item {
anchors.left: parent.left
anchors.leftMargin: 24
text: {
const formattedDate = Utils.formatShortDate(d.expirationTimestamp, localAccountSensitiveSettings.isDDMMYYDateFormat)
const formattedDate = LocaleUtils.formatDate(d.expirationTimestamp, Locale.ShortFormat)
return qsTr("Username locked. You won't be able to release it until %1").arg(formattedDate)
}
color: Style.current.darkGrey

View File

@ -10,7 +10,6 @@ import StatusQ.Components 0.1
import shared 1.0
import shared.panels 1.0
import shared.views.chat 1.0
import shared.panels.chat 1.0
import shared.controls.chat 1.0
import utils 1.0

View File

@ -46,20 +46,16 @@ SettingsContentBase {
spacing: Constants.settingsSection.itemSpacing
width: root.contentWidth
Item {
id: currency
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
height: 38
z: root.z + 2
StatusBaseText {
text: qsTr("Set Display Currency")
anchors.left: parent.left
font.pixelSize: 15
color: Theme.palette.directColor1
}
Item { Layout.fillWidth: true }
StatusListPicker {
id: currencyPicker
@ -75,9 +71,6 @@ SettingsContentBase {
}
z: root.z + 2
width: 104
height: parent.height
anchors.right: parent.right
inputList: root.currencyStore.currenciesModel
printSymbol: true
placeholderSearchText: qsTr("Search Currencies")
@ -92,23 +85,18 @@ SettingsContentBase {
}
}
Item {
id: language
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
height: 38
z: root.z + 1
StatusBaseText {
text: qsTr("Language")
anchors.left: parent.left
font.pixelSize: 15
color: Theme.palette.directColor1
}
Item { Layout.fillWidth: true }
StatusListPicker {
id: languagePicker
property string newKey
function descriptionForState(state) {
@ -161,9 +149,6 @@ SettingsContentBase {
}
z: root.z + 1
width: 104
height: parent.height
anchors.right: parent.right
placeholderSearchText: qsTr("Search Languages")
maxPickerHeight: 350
@ -175,7 +160,6 @@ SettingsContentBase {
//if (Qt.platform.os === Constants.linux) {
root.changeLanguage(key)
linuxConfirmationDialog.active = true
linuxConfirmationDialog.item.newLocale = key
linuxConfirmationDialog.item.open()
// }
@ -192,34 +176,6 @@ SettingsContentBase {
Layout.bottomMargin: Style.current.padding
}
// Date format options:
Column {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
spacing: Style.current.padding
StatusBaseText {
text: qsTr("Date Format")
anchors.left: parent.left
font.pixelSize: 15
color: Theme.palette.directColor1
}
StatusRadioButton {
text: qsTr("DD/MM/YY")
font.pixelSize: 13
checked: root.languageStore.isDDMMYYDateFormat
onToggled: root.languageStore.setIsDDMMYYDateFormat(checked)
}
StatusRadioButton {
text: qsTr("MM/DD/YY")
font.pixelSize: 13
checked: !root.languageStore.isDDMMYYDateFormat
onToggled: root.languageStore.setIsDDMMYYDateFormat(!checked)
}
}
// Time format options:
Column {
Layout.fillWidth: true
@ -229,23 +185,24 @@ SettingsContentBase {
spacing: Style.current.padding
StatusBaseText {
text: qsTr("Time Format")
anchors.left: parent.left
font.pixelSize: 15
color: Theme.palette.directColor1
}
StatusRadioButton {
text: qsTr("24-Hour Time")
StatusCheckBox {
id: use24hDefault
text: qsTr("Use System Settings")
font.pixelSize: 13
checked: root.languageStore.is24hTimeFormat
onToggled: root.languageStore.setIs24hTimeFormat(checked)
checked: LocaleUtils.settings.timeFormatUsesDefaults
onToggled: {
LocaleUtils.settings.timeFormatUsesDefaults = checked
if (checked)
LocaleUtils.settings.timeFormatUses24Hours = LocaleUtils.is24hTimeFormatDefault()
}
}
StatusRadioButton {
text: qsTr("12-Hour Time")
StatusCheckBox {
text: qsTr("Use 24-Hour Time")
font.pixelSize: 13
checked: !root.languageStore.is24hTimeFormat
onToggled: root.languageStore.setIs24hTimeFormat(!checked)
enabled: !use24hDefault.checked
checked: LocaleUtils.settings.timeFormatUses24Hours
onToggled: LocaleUtils.settings.timeFormatUses24Hours = checked
}
}
@ -254,13 +211,11 @@ SettingsContentBase {
id: linuxConfirmationDialog
active: false
sourceComponent: ConfirmationDialog {
property string newLocale
header.title: qsTr("Change language")
confirmationText: qsTr("Display language has been changed. You must restart the application for changes to take effect.")
confirmButtonLabel: qsTr("Close the app now")
onConfirmButtonClicked: {
loader.active = false
linuxConfirmationDialog.active = false
Qt.quit()
}
}

View File

@ -7,7 +7,7 @@ import "../Profile/stores"
QtObject {
id: root
property var locale: Qt.locale(localAppSettings.language)
property var locale: Qt.locale()
property var mainModuleInst: mainModule
property var aboutModuleInst: aboutModule

View File

@ -1,9 +1,11 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import StatusQ.Core 0.1
import StatusQ.Popups 0.1
import shared.stores 1.0
import utils 1.0
import StatusQ.Popups 0.1
Item {
id: appSearch
@ -75,7 +77,7 @@ Item {
searchOptionsPopupMenu: searchPopupMenu
searchResults: appSearch.store.resultModel
formatTimestampFn: function (ts) {
return Utils.formatLongDateTime(parseInt(ts, 10), RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
return LocaleUtils.formatDateTime(parseInt(ts, 10))
}
onSearchTextChanged: {
if (searchPopup.searchText !== "") {

View File

@ -156,7 +156,7 @@ Popup {
hideReadNotifications: activityCenterStore.hideReadNotifications
currentActivityCategory: root.currentActivityCategory
onCategoryTriggered: root.currentActivityCategory = category
onMarkAllReadClicked: errorText = root.activityCenterStore.markAllActivityCenterNotificationsRead()
onMarkAllReadClicked: root.activityCenterStore.markAllActivityCenterNotificationsRead()
onShowHideReadNotifications: activityCenterStore.hideReadNotifications = hideReadNotifications
}

View File

@ -1,6 +1,8 @@
import QtQuick 2.13
import QtQml 2.14
import StatusQ.Core 0.1
import utils 1.0
import shared 1.0

View File

@ -1,5 +1,6 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1

View File

@ -1,5 +1,6 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import shared.panels 1.0
@ -19,8 +20,7 @@ Item {
QtObject {
id: d
readonly property string formattedDate: nextMessageIndex > -1 ? Utils.formatLongDate(nextMsgTimestamp, RootStore.accountSensitiveSettings.isDDMMYYDateFormat) :
Utils.formatLongDate(undefined, RootStore.accountSensitiveSettings.isDDMMYYDateFormat)
readonly property string formattedDate: nextMessageIndex > -1 ? LocaleUtils.formatDate(nextMsgTimestamp) : LocaleUtils.formatDate()
}
Timer {

View File

@ -1,4 +1,7 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import shared 1.0
import shared.panels 1.0
import shared.stores 1.0
@ -44,8 +47,7 @@ Item {
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
color: Style.current.secondaryText
text: qsTr("Between %1 and %2").arg(Utils.formatLongDate(root.gapFrom * 1000, RootStore.accountSensitiveSettings.isDDMMYYDateFormat))
.arg(Utils.formatLongDate(root.gapTo * 1000, RootStore.accountSensitiveSettings.isDDMMYYDateFormat))
text: qsTr("Between %1 and %2").arg(LocaleUtils.formatDate(root.gapFrom * 1000)).arg(LocaleUtils.formatDate(root.gapTo * 1000))
}
Separator {
anchors.top: fetchDate.bottom

View File

@ -3,6 +3,8 @@ import QtQuick.Controls 2.13
import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import StatusQ.Core 0.1
import utils 1.0
import shared 1.0
import shared.popups 1.0

View File

@ -1,5 +1,7 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import utils 1.0
ChartStoreBase {
@ -75,8 +77,8 @@ ChartStoreBase {
let dataEntry = response.historicalData[i]
let dateString = response.timeInterval == ChartStoreBase.TimeRange.Weekly || response.timeInterval == ChartStoreBase.TimeRange.Monthly
? Utils.getDayMonth(dataEntry.time * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat)
: Utils.getMonthYear(dataEntry.time * 1000)
? LocaleUtils.getDayMonth(dataEntry.time * 1000)
: LocaleUtils.getMonthYear(dataEntry.time * 1000)
tmpTimeRange.push(dateString)
tmpDataValues.push(parseFloat(globalUtils.wei2Eth(dataEntry.value, 18)))
@ -86,4 +88,4 @@ ChartStoreBase {
root.updateRequestTime(response.timeInterval)
}
}
}
}

View File

@ -1,5 +1,7 @@
import QtQuick 2.13
import StatusQ.Core 0.1
import utils 1.0
ChartStoreBase {
@ -12,8 +14,8 @@ ChartStoreBase {
marketValues[i] = data[i].close;
timeRanges[i] = range === ChartStoreBase.TimeRange.Weekly || range === ChartStoreBase.TimeRange.Monthly ?
Utils.getDayMonth(data[i].time * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat):
Utils.getMonthYear(data[i].time * 1000)
LocaleUtils.getDayMonth(data[i].time * 1000):
LocaleUtils.getMonthYear(data[i].time * 1000)
}
switch(range) {

View File

@ -103,7 +103,7 @@ ColumnLayout {
StatusListItem {
property var modelData
height: 40
title: modelData !== undefined && !!modelData ? Utils.formatShortDate(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat) : ""
title: modelData !== undefined && !!modelData ? LocaleUtils.formatDate(modelData.timestamp * 1000, Locale.ShortFormat) : ""
statusListItemTitle.color: Theme.palette.baseColor1
color: Theme.palette.statusListItem.backgroundColor
sensor.enabled: false
@ -122,7 +122,7 @@ ColumnLayout {
networkName: modelData !== undefined && !!modelData ? RootStore.getNetworkShortName(modelData.chainId) : ""
symbol: modelData !== undefined && !!modelData ? !!modelData.symbol ? modelData.symbol : RootStore.findTokenSymbolByAddress(modelData.contract) : ""
transferStatus: modelData !== undefined && !!modelData ? RootStore.hex2Dec(modelData.txStatus) : ""
shortTimeStamp: modelData !== undefined && !!modelData ? Utils.formatShortTime(modelData.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat) : ""
shortTimeStamp: modelData !== undefined && !!modelData ? LocaleUtils.formatTime(modelData.timestamp * 1000, Locale.ShortFormat) : ""
savedAddressName: modelData !== undefined && !!modelData ? RootStore.getNameForSavedWalletAddress(modelData.to) : ""
onClicked: launchTransactionDetail(modelData)
}

View File

@ -198,7 +198,7 @@ Item {
implicitWidth: parent.width
height: visible ? 64 : 0
title: isIncoming ? from : to
subTitle: Utils.getTimeDifference(new Date(parseInt(timestamp) * 1000), new Date())
subTitle: LocaleUtils.getTimeDifference(new Date(parseInt(timestamp) * 1000), new Date())
statusListItemTitle.elide: Text.ElideMiddle
statusListItemTitle.wrapMode: Text.NoWrap
radius: 0

View File

@ -67,7 +67,7 @@ Item {
networkName: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkShortName(transaction.chainId): ""
symbol: root.transaction !== undefined && !!root.transaction ? RootStore.findTokenSymbolByAddress(transaction.contract): ""
transferStatus: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Dec(transaction.txStatus): ""
shortTimeStamp: root.transaction !== undefined && !!root.transaction ? Utils.formatShortTime(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat): ""
shortTimeStamp: root.transaction !== undefined && !!root.transaction ? LocaleUtils.formatTime(transaction.timestamp * 1000, Locale.ShortFormat): ""
savedAddressName: root.transaction !== undefined && !!root.transaction ? RootStore.getNameForSavedWalletAddress(transaction.to): ""
title: d.isIncoming ? qsTr("Received %1 %2 from %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.from) :
qsTr("Sent %1 %2 to %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.to)
@ -164,10 +164,10 @@ Item {
networkName: root.transaction !== undefined && !!root.transaction ? RootStore.getNetworkShortName(transaction.chainId): ""
symbol: root.transaction !== undefined && !!root.transaction ? RootStore.findTokenSymbolByAddress(transaction.contract): ""
transferStatus: root.transaction !== undefined && !!root.transaction ? RootStore.hex2Dec(transaction.txStatus): ""
shortTimeStamp: root.transaction !== undefined && !!root.transaction ? Utils.formatShortTime(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat): ""
shortTimeStamp: root.transaction !== undefined && !!root.transaction ? LocaleUtils.formatTime(transaction.timestamp * 1000, Locale.ShortFormat): ""
savedAddressName: root.transaction !== undefined && !!root.transaction ? RootStore.getNameForSavedWalletAddress(transaction.to): ""
title: d.isIncoming ? qsTr("Received %1 %2 from %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.from) :
qsTr("Sent %1 %2 to %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.to)
qsTr("Sent %1 %2 to %3").arg(cryptoValue).arg(resolvedSymbol).arg(d.to)
sensor.enabled: false
color: Theme.palette.statusListItem.backgroundColor
border.width: 1
@ -179,8 +179,8 @@ Item {
maxWidth: parent.width
primaryText: qsTr("Time")
secondaryText: root.transaction !== undefined && !!root.transaction ? qsTr("%1 <font color=\"#939BA1\">on</font> %2").
arg(Utils.formatShortTime(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat)).
arg(Utils.formatShortDate(transaction.timestamp * 1000, RootStore.accountSensitiveSettings.is24hTimeFormat)): ""
arg(LocaleUtils.formatTime(transaction.timestamp * 1000, Locale.ShortFormat)).
arg(LocaleUtils.formatDate(transaction.timestamp * 1000, Locale.ShortFormat)): ""
}
InformationTile {
maxWidth: parent.width

View File

@ -11,7 +11,6 @@ import StatusQ.Controls 0.1
import shared.status 1.0
import shared.panels 1.0
import shared.stores 1.0
import shared.panels.chat 1.0
import shared.controls.chat 1.0
Column {

View File

@ -6,7 +6,6 @@ import shared.panels 1.0
import shared.status 1.0
import shared.controls 1.0
import shared.popups 1.0
import shared.panels.chat 1.0
import shared.views.chat 1.0
import shared.controls.chat 1.0

View File

@ -4,6 +4,8 @@ import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
Item {
id: root
@ -30,7 +32,7 @@ Item {
}
StatusBaseText {
text: qsTr("%n missed message(s) since %1", "", count).arg(new Date(timestamp).toLocaleDateString())
text: qsTr("%n missed message(s) since %1", "", count).arg(LocaleUtils.formatDate(timestamp))
color: Theme.palette.primaryColor1
font.weight: Font.Bold
font.pixelSize: 13

View File

@ -25,10 +25,8 @@ RowLayout {
}
}
readonly property string timestampString: timestamp !== 0 ?
new Date(timestamp).toLocaleTimeString(Qt.locale(), Locale.ShortFormat) : ""
readonly property string timestampTooltipString: timestamp !== 0 ?
new Date(timestamp).toLocaleString() : ""
readonly property string timestampString: timestamp ? LocaleUtils.formatTime(timestamp, Locale.ShortFormat) : ""
readonly property string timestampTooltipString: timestamp ? LocaleUtils.formatDateTime(timestamp) : ""
signal openProfilePopup()
@ -97,4 +95,4 @@ RowLayout {
}
}
}
}
}

View File

@ -1,4 +1,7 @@
import QtQuick 2.3
import StatusQ.Core 0.1
import utils 1.0
import shared 1.0
import shared.panels 1.0
@ -250,7 +253,7 @@ Item {
StyledText {
id: timeText
color: Style.current.secondaryText
text: Utils.formatShortTime(messageTimestamp, RootStore.accountSensitiveSettings.is24hTimeFormat)
text: LocaleUtils.formatTime(messageTimestamp, Locale.ShortFormat)
anchors.left: bubbleLoader.active ? bubbleLoader.right : undefined
anchors.leftMargin: bubbleLoader.active ? 13 : 0
anchors.right: bubbleLoader.active ? undefined : parent.right

View File

@ -1,46 +0,0 @@
pragma Singleton
import QtQml 2.14
QtObject {
function fractionalPartLength(num) {
if (Number.isInteger(num))
return 0
return num.toString().split('.')[1].length
}
function stripTrailingZeroes(numStr, locale) {
let regEx = locale.decimalPoint == "." ? /(\.[0-9]*[1-9])0+$|\.0*$/ : /(\,[0-9]*[1-9])0+$|\,0*$/
return numStr.replace(regEx, '$1')
}
function numberToLocaleString(num, precision = -1, locale = null) {
locale = locale || Qt.locale()
if (precision === -1)
precision = fractionalPartLength(num)
return num.toLocaleString(locale, 'f', precision)
}
function currencyAmountToLocaleString(currencyAmount, locale) {
if (!locale) {
console.log("Unspecified locale for: " + JSON.stringify(currencyAmount))
locale = Qt.locale()
}
if (typeof(currencyAmount) !== "object") {
console.log("Wrong type for currencyAmount: " + JSON.stringify(currencyAmount))
return NaN
}
var amountStr = numberToLocaleString(currencyAmount.amount, currencyAmount.displayDecimals, locale)
if (currencyAmount.stripTrailingZeroes) {
amountStr = stripTrailingZeroes(amountStr, locale)
}
if (currencyAmount.symbol) {
amountStr = "%1 %2".arg(amountStr).arg(currencyAmount.symbol)
}
return amountStr
}
}

View File

@ -149,161 +149,6 @@ QtObject {
return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
}
function checkTimestamp(value, errorLocation) {
if(Number.isInteger(value) && value > 0) {
return true;
}
return false;
}
function formatTime(value, is24hTimeFormat) {
const format24h = "hh:mm:ss t"
const format12h = "h:mm:ss AP t"
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
return checkTimestamp(value, "formatTime") ? Qt.formatTime(new Date(value), currentTimeFormat) :
Qt.formatTime(new Date(), currentTimeFormat)
}
function formatShortTime(value, is24hTimeFormat) {
const format24h = "hh:mm"
const format12h = "h:mm AP"
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
return checkTimestamp(value, "formatShortTime") ? Qt.formatTime(new Date(value), currentTimeFormat) :
Qt.formatTime(new Date(), currentTimeFormat)
}
function formatShortDateStr(longStr) {
const dmKeys = {
// Days
Sunday: qsTr("Sun"),
Monday: qsTr("Mon"),
Tuesday: qsTr("Tue"),
Wednesday: qsTr("Wed"),
Thursday: qsTr("Thu"),
Friday: qsTr("Fri"),
Saturday: qsTr("Sat"),
// Months
January: qsTr("Jan"),
February: qsTr("Feb"),
March: qsTr("Mar"),
April: qsTr("Apr"),
May: qsTr("May"),
June: qsTr("Jun"),
July: qsTr("Jul"),
August: qsTr("Aug"),
September: qsTr("Sep"),
October: qsTr("Oct"),
November: qsTr("Nov"),
December: qsTr("Dec")
};
let shortStr = longStr;
for (const [key, value] of Object.entries(dmKeys)) {
shortStr = shortStr.replace(key, value);
shortStr = shortStr.replace(key.toLowerCase(), value);
shortStr = shortStr.replace(key.toUpperCase(), value);
}
return shortStr;
}
function formatLongDate(value, isDDMMYYDateFormat) {
const formatDDMMYY = "dddd d MMMM yyyy"
const formatMMDDYY = "dddd, MMMM d, yyyy"
const currentFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
return checkTimestamp(value, "formatLongDate") ? Qt.formatDate(new Date(value), currentFormat) :
Qt.formatDate(new Date(), currentFormat)
}
function formatLongDateTime(value, isDDMMYYDateFormat, is24hTimeFormat) {
const formatDDMMYY = "dddd d MMMM yyyy"
const formatMMDDYY = "dddd, MMMM d, yyyy"
const format24h = "hh:mm:ss t"
const format12h = "h:mm:ss AP t"
const currentDateFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
return checkTimestamp(value, "formatLongDateTime") ? Qt.formatDateTime(new Date(value), currentDateFormat + " " + currentTimeFormat) :
Qt.formatDateTime(new Date(), currentDateFormat + " " + currentTimeFormat)
}
// WARN: It is not used!! TO BE REMOVE??
function formatDateTime(timestamp, locale) {
let now = new Date()
let yesterday = new Date()
yesterday.setDate(now.getDate()-1)
let messageDate = new Date(Math.floor(timestamp))
let lastWeek = new Date()
lastWeek.setDate(now.getDate()-7)
let minutes = messageDate.getMinutes();
let hours = messageDate.getHours();
if (now.toDateString() === messageDate.toDateString()) {
return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes)
} else if (yesterday.toDateString() === messageDate.toDateString()) {
return qsTr("Yesterday")
} else if (lastWeek.getTime() < messageDate.getTime()) {
let days = [qsTr("Sunday"),
qsTr("Monday"),
qsTr("Tuesday"),
qsTr("Wednesday"),
qsTr("Thursday"),
qsTr("Friday"),
qsTr("Saturday")];
return days[messageDate.getDay()];
} else {
return formatShortDateStr(new Date().toLocaleDateString(Qt.locale(locale)))
}
}
// WARN: It is not used!! TO BE REMOVE??
function formatAgeFromTime(timestamp, epoch) {
epoch++ // pretending the parameter is not unused
const now = new Date()
const messageDate = new Date(Math.floor(timestamp))
const diffMs = now - messageDate
const diffMin = Math.floor(diffMs / 60000)
if (diffMin < 1) {
return qsTr("NOW")
}
const diffHour = Math.floor(diffMin / 60)
if (diffHour < 1) {
return qsTr("%1M").arg(diffMin)
}
const diffDay = Math.floor(diffHour / 24)
if (diffDay < 1) {
return qsTr("%1H").arg(diffHour)
}
return qsTr("%1D").arg(diffDay)
}
function getDayMonth(value, isDDMMYYDateFormat) {
const formatDDMMYY = "d MMMM"
const formatMMDDYY = "MMMM d"
const currentFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
var timeStamp = checkTimestamp(value, "formatLongDate") ? Qt.formatDate(new Date(value), currentFormat) :
Qt.formatDate(new Date(), currentFormat)
return formatShortDateStr(timeStamp)
}
function getMonthYear(value) {
const formatDDMMYY = "MMMM yyyy"
var timeStamp = checkTimestamp(value, "formatLongDate") ? Qt.formatDate(new Date(value), formatDDMMYY) :
Qt.formatDate(new Date(), formatDDMMYY)
return formatShortDateStr(timeStamp)
}
function formatShortDate(value, isDDMMYYDateFormat) {
const formatDDMMYY = "d MMMM yyyy"
const formatMMDDYY = "MMMM d yyyy"
const currentFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
var timeStamp = checkTimestamp(value, "formatLongDate") ? Qt.formatDate(new Date(value), currentFormat) :
Qt.formatDate(new Date(), currentFormat)
return formatShortDateStr(timeStamp)
}
// To-do move to Wallet Store, this should not be under Utils.
function findAssetByChainAndSymbol(chainIdToFind, assets, symbolToFind) {
for(var i=0; i<assets.rowCount(); i++) {
@ -668,68 +513,6 @@ QtObject {
return getElidedPk(compressedPk, 6, 3)
}
function getTimeDifference(d1, d2) {
var timeString = ""
var day1Year = d1.getFullYear()
var day1Month = d1.getMonth()
var day1Time = d1.getTime()
var day2Year = d2.getFullYear()
var day2Month = d2.getMonth()
var day2Time = d2.getTime()
var inYears = day2Year-day1Year
if(inYears > 0) {
timeString = inYears > 1 ? qsTr("years ago") : qsTr("year ago")
return inYears + " " + timeString
}
var inMonths = (day2Month+12*day2Year)-(day1Month+12*day1Year)
if(inMonths > 0) {
timeString = inMonths > 1 ? qsTr("months ago") : qsTr("month ago")
return inMonths + " " + timeString
}
var inWeeks = parseInt((day2Time-day2Time)/(24*3600*1000*7))
if(inWeeks > 0) {
timeString = inWeeks > 1 ? qsTr("weeks ago") : qsTr("week ago")
return inWeeks + " " + timeString
}
var inDays = parseInt((day2Time-day1Time)/(24*3600*1000))
if(inDays > 0) {
timeString = inDays > 1 ? qsTr("days ago") : qsTr("day ago")
return inDays + " " + timeString
}
var inHours = parseInt((day2Time-day1Time)/(3600*1000));
if(inHours > 0) {
timeString = inHours > 1 ? qsTr("hours ago") : qsTr("hour ago")
return inHours + " " + timeString
}
var inMins = parseInt((day2Time-day1Time)/(60*1000))
if(inMins > 0) {
timeString = inMins > 1 ? qsTr("mins ago") : qsTr("min ago")
return inMins + " " + timeString
}
var inSecs = parseInt((day2Time-day1Time)/(1000));
if(inSecs > 0) {
timeString = inSecs > 1 ? qsTr("secs ago") : qsTr("sec ago")
return inSecs + " " + timeString
}
return qsTr("now")
}
function elideIfTooLong(str, maxLength) {
return (str.length > maxLength) ? str.substr(0, maxLength-4) + '...' : str;
}

View File

@ -3,7 +3,6 @@ Tracer 1.0 Tracer.qml
singleton Backpressure 1.0 Backpressure/Backpressure.qml
singleton Constants 1.0 Constants.qml
singleton Global 1.0 Global.qml
singleton LocaleUtils 1.0 LocaleUtils.qml
singleton SelectedMessage 1.0 SelectedMessage.qml
singleton Style 1.0 Style.qml
singleton Utils 1.0 Utils.qml