feat(Language&Currency): Add Date & Time Format

Added Date & Time selectors in `Language & Currency" screen.

Added new properties in `local_app_settings` to set the date and time format.

Created all the chain from view to service to update date and time format.

Added date and time format functions in `Utils`.

Updated `FetchMoreMessagesButton`, `ChatTimePanel`,  `AppSearch`, `TransactionBubbleView` and `TransactionDelegate` date and time text depending on the selected format.

Closes #5386
This commit is contained in:
Noelia 2022-04-01 12:30:55 +02:00 committed by Noelia
parent a5be90761b
commit 2154626a5b
16 changed files with 263 additions and 90 deletions

View File

@ -100,6 +100,10 @@ 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
QtObject:
type LocalAccountSensitiveSettings* = ref object of QObject
@ -805,6 +809,30 @@ 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 removeKey*(self: LocalAccountSensitiveSettings, key: string) =
if(self.settings.isNil):
@ -863,3 +891,6 @@ 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()

View File

@ -102,4 +102,4 @@ QtObject:
case key:
of LAS_KEY_LOCALE: self.localeChanged()
of LAS_KEY_THEME: self.themeChanged()
of LAS_KEY_THEME: self.themeChanged()

View File

@ -20,4 +20,4 @@ proc delete*(self: Controller) =
discard
proc changeLanguage*(self: Controller, locale: string) =
self.languageService.setLanguage(locale)
self.languageService.setLanguage(locale)

View File

@ -19,6 +19,12 @@ method getModuleAsVariant*(self: AccessInterface): QVariant {.base.} =
method changeLanguage*(self: AccessInterface, locale: 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")
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi
# inheritance, which is not well supported in Nim.

View File

@ -43,3 +43,11 @@ method getModuleAsVariant*(self: Module): QVariant =
method changeLanguage*(self: Module, locale: string) =
self.controller.changeLanguage(locale)
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

@ -21,3 +21,9 @@ QtObject:
proc changeLocale*(self: View, locale: string) {.slot.} =
self.delegate.changeLanguage(locale)
proc setIsDDMMYYDateFormat*(self: View, isDDMMYYDateFormat: bool) {.slot.} =
self.delegate.setIsDDMMYYDateFormat(isDDMMYYDateFormat)
proc setIs24hTimeFormat*(self: View, is24hTimeFormat: bool) {.slot.} =
self.delegate.setIs24hTimeFormat(is24hTimeFormat)

View File

@ -8,13 +8,13 @@ import utils 1.0
StyledText {
id: chatTime
color: Style.current.secondaryText
text: Utils.formatTime(timestamp)
text: Utils.formatShortTime(timestamp)
font.pixelSize: Style.current.asideTextFontSize
//property string timestamp
property string timestamp
StatusQ.StatusToolTip {
visible: hhandler.hovered
text: new Date(parseInt(timestamp, 10)).toLocaleString(Qt.locale(localAppSettings.locale))
text: Utils.formatLongDateTime(parseInt(chatTime.timestamp, 10), RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
maxWidth: 350
}

View File

@ -5,28 +5,11 @@ QtObject {
id: root
property var languageModule
property string locale: localAppSettings.locale
property bool isDDMMYYDateFormat: localAccountSensitiveSettings.isDDMMYYDateFormat
property bool is24hTimeFormat: localAccountSensitiveSettings.is24hTimeFormat
function changeLocale(locale) {
root.languageModule.changeLocale(locale)
}
function initializeLanguageModel() {
var isSelected = false
for(var i = 0; i < languageModel.count; i++) {
if(localAppSettings.locale === root.languageModel.get(i).key) {
isSelected = true
root.languageModel.get(i).selected = true
}
else {
root.languageModel.get(i).selected = false
}
}
// Set default:
if(!isSelected)
root.languageModel.get(0).selected = true
}
// TODO: That definition should be moved to backend.
property ListModel languageModel: ListModel {
ListElement {key: "en"; shortName: "English"; name: "English"; category: ""; imageSource: "../../assets/twemoji/26x26/1f1ec-1f1e7.png"; selected: false}
ListElement {key: "zh"; shortName: "普通话"; name: "Chinese (Mainland China)"; imageSource: "../../assets/twemoji/26x26/1f1e8-1f1f3.png"; category: ""; selected: false}
@ -44,4 +27,34 @@ QtObject {
ListElement {key: "tr"; shortName: "Türkçe"; name: "Turkish"; category: "Beta Languages"; imageSource: "../../assets/twemoji/26x26/1f1f9-1f1f7.png"; selected: false}
ListElement {key: "ur"; shortName: "اُردُو"; name: "Urdu"; category: "Beta Languages"; imageSource: "../../assets/twemoji/26x26/1f1f5-1f1f0.png"; selected: false}
}
// TODO: That logic should be moved to backend.
function initializeLanguageModel() {
var isSelected = false
for(var i = 0; i < languageModel.count; i++) {
if(localAppSettings.locale === root.languageModel.get(i).key) {
isSelected = true
root.languageModel.get(i).selected = true
}
else {
root.languageModel.get(i).selected = false
}
}
// Set default:
if(!isSelected)
root.languageModel.get(0).selected = true
}
function changeLocale(locale) {
root.languageModule.changeLocale(locale)
}
function setIsDDMMYYDateFormat(isDDMMYYDateFormat) {
root.languageModule.setIsDDMMYYDateFormat(isDDMMYYDateFormat)
}
function setIs24hTimeFormat(is24hTimeFormat) {
root.languageModule.setIs24hTimeFormat(is24hTimeFormat)
}
}

View File

@ -9,6 +9,7 @@ import shared.popups 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import "../popups"
import "../stores"
@ -19,35 +20,30 @@ SettingsContentBase {
property LanguageStore languageStore
property var currencyStore
onVisibleChanged: { if(!visible) d.setViewIdleState()}
onVisibleChanged: { if(!visible) root.setViewIdleState()}
onBaseAreaClicked: { root.setViewIdleState() }
Component.onCompleted: {
root.currencyStore.updateCurrenciesModel()
root.languageStore.initializeLanguageModel()
}
function setViewIdleState() {
currencyPicker.close()
languagePicker.close()
}
ColumnLayout {
spacing: Constants.settingsSection.itemSpacing
width: root.contentWidth
QtObject {
id: d
property int margins: 64
property int zOnTop: 100
function setViewIdleState() {
currencyPicker.close()
languagePicker.close()
}
}
Item {
id: currency
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
height: 38
z: d.zOnTop + 1
z: root.z + 2
StatusBaseText {
text: qsTr("Set Display Currency")
@ -69,13 +65,14 @@ SettingsContentBase {
}
}
z: d.zOnTop + 1
z: root.z + 2
width: 104
height: parent.height
anchors.right: parent.right
inputList: root.currencyStore.currenciesModel
printSymbol: true
placeholderSearchText: qsTr("Search Currencies")
maxPickerHeight: 350
onItemPickerChanged: {
if(selected) {
@ -92,7 +89,7 @@ SettingsContentBase {
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
height: 38
z: d.zOnTop
z: root.z + 1
StatusBaseText {
text: qsTr("Language")
@ -114,12 +111,13 @@ SettingsContentBase {
}
}
z: d.zOnTop
z: root.z + 1
width: 104
height: parent.height
anchors.right: parent.right
inputList: root.languageStore.languageModel
placeholderSearchText: qsTr("Search Languages")
maxPickerHeight: 350
onItemPickerChanged: {
if(selected && localAppSettings.locale !== key) {
@ -140,8 +138,75 @@ SettingsContentBase {
Separator {
Layout.fillWidth: true
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 {
id: ddmmyyFormat
ButtonGroup.group: dateFormatGroup
text: qsTr("DD/MM/YY")
font.pixelSize: 13
checked: root.languageStore.isDDMMYYDateFormat
onCheckedChanged: root.languageStore.setIsDDMMYYDateFormat(checked)
}
StatusRadioButton {
id: mmddyyFormat
ButtonGroup.group: dateFormatGroup
text: qsTr("MM/DD/YY")
font.pixelSize: 13
checked: !root.languageStore.isDDMMYYDateFormat
}
ButtonGroup { id: dateFormatGroup }
}
// Time format options:
Column {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
Layout.topMargin: Style.current.padding
spacing: Style.current.padding
StatusBaseText {
text: qsTr("Time Format")
anchors.left: parent.left
font.pixelSize: 15
color: Theme.palette.directColor1
}
StatusRadioButton {
id: h24Format
ButtonGroup.group: timeFormatGroup
text: qsTr("24-Hour Time")
font.pixelSize: 13
checked: root.languageStore.is24hTimeFormat
onCheckedChanged: root.languageStore.setIs24hTimeFormat(checked)
}
StatusRadioButton {
id: h12Format
ButtonGroup.group: timeFormatGroup
text: qsTr("12-Hour Time")
font.pixelSize: 13
checked: !root.languageStore.is24hTimeFormat
}
ButtonGroup { id: timeFormatGroup }
}
// TEMPORARY: It should be removed as it is only used in Linux OS but it must be investigated how to change language in execution time, as well, in Linux (will be addressed in another task)
Loader {
@ -160,11 +225,6 @@ SettingsContentBase {
}
}
}
// Outsite area
MouseArea {
anchors.fill: parent
onClicked: { d.setViewIdleState() }
}
}
}

View File

@ -8,7 +8,7 @@ import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
Item {
MouseArea {
id: root
property string sectionTitle
@ -21,6 +21,7 @@ Item {
default property Item content
signal backButtonClicked()
signal baseAreaClicked()
QtObject {
id: d
@ -29,6 +30,8 @@ Item {
readonly property int titleRowHeight: 56
}
onClicked: { root.baseAreaClicked() }
Component.onCompleted: {
content.parent = contentWrapper
@ -89,10 +92,17 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: Style.current.bigPadding
contentWidth: Math.max(contentWrapper.implicitWidth, width)
contentHeight: Math.max(contentWrapper.implicitHeight, height)
clip: true
Column {
id: contentWrapper
MouseArea {
anchors.fill: parent
onClicked: { root.baseAreaClicked() }
Column {
id: contentWrapper
}
}
}
}

View File

@ -1,6 +1,7 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import shared.stores 1.0
import utils 1.0
import StatusQ.Popups 0.1
@ -50,17 +51,13 @@ Item {
StatusSearchPopup {
id: searchPopup
noResultsLabel: qsTr("No results")
defaultSearchLocationText: qsTr("Anywhere")
searchOptionsPopupMenu: searchPopupMenu
searchResults: appSearch.store.resultModel
formatTimestampFn: function (ts) {
return new Date(parseInt(ts, 10)).toLocaleString(Qt.locale(localAppSettings.locale))
return Utils.formatLongDateTime(parseInt(ts, 10), RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
}
onSearchTextChanged: {
if (searchPopup.searchText !== "") {
searchPopup.loading = true

View File

@ -3,6 +3,7 @@ import QtQuick 2.13
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.stores 1.0
Rectangle {
@ -142,7 +143,7 @@ Rectangle {
}
StyledText {
id: timeValue
text: new Date(parseInt(timestamp) * 1000).toLocaleString(locale)
text: Utils.formatLongDateTime(parseInt(timestamp) * 1000, RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
font.pixelSize: Style.current.primaryTextFontSize
anchors.rightMargin: Style.current.smallPadding
}

View File

@ -15,6 +15,13 @@ Item {
property string nextMsgTimestamp
signal clicked()
signal timerTriggered()
QtObject {
id: d
property string formattedDate: nextMessageIndex > -1 ? Utils.formatLongDate(nextMsgTimestamp * 1, RootStore.accountSensitiveSettings.isDDMMYYDateFormat) :
Utils.formatLongDate(undefined, RootStore.accountSensitiveSettings.isDDMMYYDateFormat)
}
Timer {
id: timer
interval: 3000
@ -72,8 +79,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
color: Style.current.secondaryText
//% "before %1"
text: qsTrId("before--1").arg((nextMessageIndex > -1 ? new Date(nextMsgTimestamp * 1) : new Date()).toLocaleString(Qt.locale(RootStore.locale)))
text: qsTr("before--%1").arg(d.formattedDate)
}
Separator {
anchors.top: fetchDate.bottom

View File

@ -8,14 +8,16 @@ import utils 1.0
StyledText {
id: chatTime
color: Style.current.secondaryText
property string timestamp
text: Utils.formatTime(chatTime.timestamp)
color: Style.current.secondaryText
text: Utils.formatShortTime(chatTime.timestamp, RootStore.accountSensitiveSettings.is24hTimeFormat)
font.pixelSize: Style.current.asideTextFontSize
StatusQ.StatusToolTip {
visible: hhandler.hovered
text: new Date(parseInt(chatTime.timestamp, 10)).toLocaleString(Qt.locale(RootStore.locale))
text: Utils.formatLongDateTime(parseInt(chatTime.timestamp, 10), RootStore.accountSensitiveSettings.isDDMMYYDateFormat, RootStore.accountSensitiveSettings.is24hTimeFormat)
maxWidth: 350
}

View File

@ -6,6 +6,7 @@ import shared.popups 1.0
import shared.views.chat 1.0
import shared.controls.chat 1.0
import shared.controls 1.0
import shared.stores 1.0
Item {
id: root
@ -252,7 +253,7 @@ Item {
StyledText {
id: timeText
color: Style.current.secondaryText
text: Utils.formatTime(timestamp)
text: Utils.formatShortTime(timestamp, RootStore.accountSensitiveSettings.is24hTimeFormat)
anchors.left: bubbleLoader.active ? bubbleLoader.right : undefined
anchors.leftMargin: bubbleLoader.active ? 13 : 0
anchors.right: bubbleLoader.active ? undefined : parent.right

View File

@ -224,35 +224,22 @@ QtObject {
return Qt.hsla(color.hslHue, color.hslSaturation, color.hslLightness, alpha)
}
function formatTime(timestamp) {
let messageDate = new Date(Math.floor(timestamp))
let minutes = messageDate.getMinutes();
let hours = messageDate.getHours();
return (hours < 10 ? "0" + hours : hours) + ":" + (minutes < 10 ? "0" + minutes : minutes)
function formatTime(value, is24hTimeFormat) {
const format24h = "hh:mm:ss t"
const format12h = "h:mm:ss AP t"
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
return !!value ? Qt.formatTime(new Date(), currentTimeFormat) :
Qt.formatTime(new Date(value), currentTimeFormat)
}
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) {
//% "NOW"
return qsTrId("now")
}
const diffHour = Math.floor(diffMin / 60)
if (diffHour < 1) {
//% "%1M"
return qsTrId("-1m").arg(diffMin)
}
const diffDay = Math.floor(diffHour / 24)
if (diffDay < 1) {
//% "%1H"
return qsTrId("-1h").arg(diffHour)
}
//% "%1D"
return qsTrId("-1d").arg(diffDay)
function formatShortTime(value, is24hTimeFormat) {
const format24h = "hh:mm"
const format12h = "h:mm AP"
const currentTimeFormat = is24hTimeFormat ? format24h : format12h
return !!value ? Qt.formatTime(new Date(), currentTimeFormat) :
Qt.formatTime(new Date(value), currentTimeFormat)
}
function formatShortDateStr(longStr) {
@ -309,6 +296,26 @@ QtObject {
return shortStr;
}
function formatLongDate(value, isDDMMYYDateFormat) {
const formatDDMMYY = "dddd d MMMM yyyy"
const formatMMDDYY = "dddd, MMMM d, yyyy"
const currentFormat = isDDMMYYDateFormat ? formatDDMMYY : formatMMDDYY
return !!value ? Qt.formatDate(new Date(), currentFormat) :
Qt.formatDate(new Date(value), 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 !!value ? Qt.formatDateTime(new Date(), currentDateFormat + " " + currentTimeFormat) :
Qt.formatDateTime(new Date(value), currentDateFormat + " " + currentTimeFormat)
}
// WARN: It is not used!! TO BE REMOVE??
function formatDateTime(timestamp, locale) {
let now = new Date()
let yesterday = new Date()
@ -346,6 +353,31 @@ QtObject {
}
}
// 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) {
//% "NOW"
return qsTrId("now")
}
const diffHour = Math.floor(diffMin / 60)
if (diffHour < 1) {
//% "%1M"
return qsTrId("-1m").arg(diffMin)
}
const diffDay = Math.floor(diffHour / 24)
if (diffDay < 1) {
//% "%1H"
return qsTrId("-1h").arg(diffHour)
}
//% "%1D"
return qsTrId("-1d").arg(diffDay)
}
function findAssetBySymbol(assets, symbolToFind) {
for(var i=0; i<assets.rowCount(); i++) {
const symbol = assets.rowData(i, "symbol")