Fixes on chat/history/conferences section date timezone and conferences creation timezone:

- Remove timezone variable as it is not stored into database. Use it only when building the conference date (which is in UTC).
- Replace javascript managment on dates by Qt in C++ because of timezone complexity that can be different from C++ and QML.
- Remove the buggy autocheck on conference creation time.
This commit is contained in:
Julien Wadel 2023-05-23 18:36:37 +02:00
parent 92297632f4
commit f5b59f5633
12 changed files with 80 additions and 126 deletions

View file

@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- File viewer in chats (Image/Animated Image/Video/Texts) with the option to export the file.
- Accept/decline CLI commands.
## 5.0.17 - undefined
### Fixed
- Section date timezone and conferences timezone.
## 5.0.16 - 2023-05-12
### Fixed

View file

@ -80,13 +80,9 @@ QSharedPointer<ConferenceInfoModel> ConferenceInfoModel::create(std::shared_ptr<
// Callable from QML
ConferenceInfoModel::ConferenceInfoModel (QObject * parent) : QObject(parent){
mTimeZone = QTimeZone::systemTimeZone();
mConferenceInfo = linphone::Factory::get()->createConferenceInfo();
QDateTime currentDateTime = QDateTime::currentDateTime();
QDateTime utc = currentDateTime.addSecs( -mTimeZone.offsetFromUtc(currentDateTime));
mConferenceInfo->setDateTime(0);
mConferenceInfo->setDuration(0);
mIsScheduled = false;
initDateTime();
auto defaultAccount = CoreManager::getInstance()->getCore()->getDefaultAccount();
if(defaultAccount){
auto accountAddress = defaultAccount->getContactAddress();
@ -96,7 +92,6 @@ ConferenceInfoModel::ConferenceInfoModel (QObject * parent) : QObject(parent){
mConferenceInfo->setOrganizer(cleanedClonedAddress);
}
}
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::timeZoneModelChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::dateTimeChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::durationChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::organizerChanged);
@ -113,11 +108,9 @@ ConferenceInfoModel::ConferenceInfoModel (QObject * parent) : QObject(parent){
// Callable from C++
ConferenceInfoModel::ConferenceInfoModel (std::shared_ptr<linphone::ConferenceInfo> conferenceInfo, QObject * parent) : QObject(parent){
App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
mTimeZone = QTimeZone::systemTimeZone();
mConferenceInfo = conferenceInfo;
mIsScheduled = (mConferenceInfo->getDateTime() != 0 || mConferenceInfo->getDuration() != 0);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::timeZoneModelChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::dateTimeChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::durationChanged);
connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::organizerChanged);
@ -144,15 +137,25 @@ std::shared_ptr<linphone::ConferenceInfo> ConferenceInfoModel::findConferenceInf
//------------------------------------------------------------------------------------------------
//Note conferenceInfo->getDateTime uses UTC
QDateTime ConferenceInfoModel::getDateTimeSystem() const{
return QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000, QTimeZone::systemTimeZone());
void ConferenceInfoModel::initDateTime(){
if(!mIsScheduled){
setDateTime(QDateTime::fromMSecsSinceEpoch(0));
setDuration(0);
}else{
setDateTime(QDateTime::currentDateTimeUtc());
setDuration(1200);
}
}
//Note conferenceInfo->getDateTime uses UTC
QDateTime ConferenceInfoModel::getDateTimeUtc() const{
return getDateTimeSystem().toUTC();
}
QDateTime ConferenceInfoModel::getDateTimeSystem() const{
return QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000, QTimeZone::systemTimeZone());
}
int ConferenceInfoModel::getDuration() const{
return mConferenceInfo->getDuration();
}
@ -232,7 +235,7 @@ int ConferenceInfoModel::getAllParticipantCount()const{
}
TimeZoneModel* ConferenceInfoModel::getTimeZoneModel() const{
TimeZoneModel * model = new TimeZoneModel(mTimeZone);
TimeZoneModel * model = new TimeZoneModel(QTimeZone::systemTimeZone());// Always return system timezone because this info is not stored in database.
App::getInstance()->getEngine()->setObjectOwnership(model, QQmlEngine::JavaScriptOwnership);
return model;
}
@ -256,6 +259,11 @@ void ConferenceInfoModel::setDateTime(const QDateTime& dateTime){
emit dateTimeChanged();
}
void ConferenceInfoModel::setDateTime(const QDate& date, const QTime& time, TimeZoneModel * model){
setIsScheduled(true);
setDateTime(QDateTime(date, time, model->getTimeZone()));
}
void ConferenceInfoModel::setDuration(const int& duration){
mConferenceInfo->setDuration(duration);
emit durationChanged();
@ -281,28 +289,9 @@ void ConferenceInfoModel::setParticipants(ParticipantListModel * participants){
emit participantsChanged();
}
void ConferenceInfoModel::setTimeZoneModel(TimeZoneModel * model){
if( mTimeZone != model->getTimeZone()){
mTimeZone = model->getTimeZone();
emit timeZoneModelChanged();
}
}
void ConferenceInfoModel::setIsScheduled(const bool& on){
if( mIsScheduled != on){
mIsScheduled = on;
if(!mIsScheduled){
mConferenceInfo->setDateTime(0);
mConferenceInfo->setDuration(0);
}else{
mTimeZone = QTimeZone::systemTimeZone();
QDateTime currentDateTime = QDateTime::currentDateTime();
QDateTime utc = currentDateTime.addSecs( -mTimeZone.offsetFromUtc(currentDateTime));
mConferenceInfo->setDateTime(utc.toMSecsSinceEpoch() / 1000);
mConferenceInfo->setDuration(1200);
}
emit dateTimeChanged();
emit durationChanged();
emit isScheduledChanged();
}
}
@ -324,9 +313,8 @@ void ConferenceInfoModel::setConferenceInfo(std::shared_ptr<linphone::Conference
void ConferenceInfoModel::resetConferenceInfo() {
mConferenceInfo = linphone::Factory::get()->createConferenceInfo();
mConferenceInfo->setDateTime(0);
mConferenceInfo->setDuration(0);
mIsScheduled = false;
initDateTime();
auto defaultAccount = CoreManager::getInstance()->getCore()->getDefaultAccount();
if(defaultAccount){
auto accountAddress = defaultAccount->getContactAddress();

View file

@ -37,7 +37,7 @@ class ConferenceInfoModel : public QObject {
Q_OBJECT
public:
Q_PROPERTY(TimeZoneModel * timeZoneModel READ getTimeZoneModel WRITE setTimeZoneModel NOTIFY timeZoneModelChanged)
Q_PROPERTY(TimeZoneModel * timeZoneModel READ getTimeZoneModel CONSTANT)
Q_PROPERTY(QDateTime dateTime READ getDateTimeSystem WRITE setDateTime NOTIFY dateTimeChanged)
Q_PROPERTY(QDateTime dateTimeUtc READ getDateTimeUtc NOTIFY dateTimeChanged)
Q_PROPERTY(int duration READ getDuration WRITE setDuration NOTIFY durationChanged)
@ -62,7 +62,8 @@ public:
static std::shared_ptr<linphone::ConferenceInfo> findConferenceInfo(const std::shared_ptr<const linphone::ConferenceInfo> & conferenceInfo);
//-------------------------------
Q_INVOKABLE void initDateTime();
QDateTime getDateTimeUtc() const;
QDateTime getDateTimeSystem() const;
int getDuration() const;
@ -91,8 +92,8 @@ public:
void setIsScheduled(const bool& on);
void setInviteMode(const int& modes);
Q_INVOKABLE void setDateTime(const QDate& date, const QTime& time, TimeZoneModel * model);
Q_INVOKABLE void setParticipants(ParticipantListModel * participants);
Q_INVOKABLE void setTimeZoneModel(TimeZoneModel * model);
void setConferenceInfo(std::shared_ptr<linphone::ConferenceInfo> conferenceInfo);
// Tools
@ -107,7 +108,6 @@ public:
virtual void onInvitationsSent(const std::list<std::shared_ptr<linphone::Address>> & failedInvitations);
signals:
void timeZoneModelChanged();
void dateTimeChanged();
void durationChanged();
void organizerChanged();
@ -129,7 +129,6 @@ signals:
private:
std::shared_ptr<linphone::ConferenceInfo> mConferenceInfo;
QSharedPointer<ConferenceScheduler> mConferenceScheduler= nullptr;
QTimeZone mTimeZone;
bool mIsScheduled = true;
int mInviteMode = 0;

View file

@ -114,8 +114,8 @@ QString Utils::toTimeString(QDateTime date, const QString& format){
return getOffsettedUTC(date).toString(format);
}
QString Utils::toDateString(QDateTime date){
return getOffsettedUTC(date).toString("yyyy/MM/dd");
QString Utils::toDateString(QDateTime date, const QString& format){
return QLocale().toString(getOffsettedUTC(date), (!format.isEmpty() ? format : QLocale().dateFormat()) );
}
QString Utils::getDisplayName(const QString& address){
@ -702,4 +702,4 @@ QString Utils::encodeTextToQmlRichFormat(const QString& text, const QVariantMap&
images = "<div>" + images +"</div>";
return images + "<p style=\"white-space:pre-wrap;\">" + formattedText.join("") + "</p>";
}
}

View file

@ -57,7 +57,7 @@ public:
static QDateTime getOffsettedUTC(const QDateTime& date);
Q_INVOKABLE static QString toDateTimeString(QDateTime date);
Q_INVOKABLE static QString toTimeString(QDateTime date, const QString& format = "hh:mm:ss");
Q_INVOKABLE static QString toDateString(QDateTime date);
Q_INVOKABLE static QString toDateString(QDateTime date, const QString& format = "");
Q_INVOKABLE static QString getDisplayName(const QString& address);
Q_INVOKABLE static QString getInitials(const QString& username); // Support UTF32
Q_INVOKABLE static QString toString(const LinphoneEnums::TunnelMode& mode);

View file

@ -143,7 +143,7 @@ Item{
text: {
if(cellItem.day < 0){
// Magic date to set day names in this order : 'S', 'M', 'T', 'W', 'T', 'F', 'S' in Locale
return Utils.exactDate(new Date(2000,9,index+1)).toLocaleString(App.locale, 'ddd')[0].toUpperCase()
return App.locale.dayName(index)[0].toUpperCase()
}else if(cellItem.cellDate.getMonth() == month && (!hideOldDates || new Date(year, month, cellItem.date+1) >= new Date())) // new Date use time too
return cellItem.date
else
@ -168,4 +168,4 @@ Item{
}
}
}
}
}

View file

@ -8,27 +8,29 @@ import Units 1.0
Item{
id: mainItem
property date selectedTime
property string selectedTime
property int border: 25
property int centerPosition: Math.min(height, width)/2
property int middleMinSize: centerPosition - border // Minus border
signal newDate(date date)
signal clicked(date date)
signal newTime(string time)
signal clicked(string time)
onNewDate: selectedTime = date
onNewTime: selectedTime = time
function getDate(hText, mText){
var d = new Date()
if(hText || outer.currentItem)
d.setHours(hText ? hText : outer.currentItem.text)
if(mText || inner.currentItem)
d.setMinutes(mText ? mText : inner.currentItem.text)
d.setSeconds(0)
return d;
function getTime(hText, mText){
return (hText ? hText : outer.currentItem.text)+':'+(mText ? mText : inner.currentItem.text)
}
function getHours(time){
var partsArray = time.split(':');
return partsArray[0]
}
function getMinutes(time){
var partsArray = time.split(':');
return partsArray[1]
}
PathView {
id: outer
model: 24
@ -39,9 +41,9 @@ Item{
currentIndex: 0
Connections{// startX/Y begin from currentIndex. It must be set to 0 at first.
target: mainItem
onSelectedTimeChanged: outer.currentIndex = mainItem.selectedTime.getHours() % 24
onSelectedTimeChanged: outer.currentIndex = mainItem.getHours(mainItem.selectedTime) % 24
}
Component.onCompleted: currentIndex = mainItem.selectedTime.getHours() % 24
Component.onCompleted: currentIndex = mainItem.getHours(mainItem.selectedTime) % 24
highlight: Rectangle {
id: rect
@ -68,7 +70,7 @@ Item{
}
MouseArea {
anchors.fill: parent
onClicked: mainItem.selectedTime = mainItem.getDate(parent.text, undefined)
onClicked: mainItem.selectedTime = mainItem.getTime(parent.text, undefined)
}
}
@ -100,9 +102,9 @@ Item{
currentIndex: 0
Connections{// startX/Y begin from currentIndex. It must be set to 0 at first.
target: mainItem
onSelectedTimeChanged: inner.currentIndex = mainItem.selectedTime.getMinutes() / 5
onSelectedTimeChanged: inner.currentIndex = mainItem.getMinutes(mainItem.selectedTime) / 5
}
Component.onCompleted: currentIndex = mainItem.selectedTime.getMinutes() / 5
Component.onCompleted: currentIndex = mainItem.getMinutes(mainItem.selectedTime) / 5
highlight: Rectangle {
width: 30 * 1.5
@ -128,7 +130,7 @@ Item{
MouseArea {
anchors.fill: parent
onClicked: mainItem.selectedTime = mainItem.getDate(undefined, parent.text)
onClicked: mainItem.selectedTime = mainItem.getTime(undefined, parent.text)
}
}
@ -176,8 +178,8 @@ Item{
MouseArea {
anchors.fill: selectedTimeArea
onClicked: {
mainItem.selectedTime = mainItem.getDate()
mainItem.selectedTime = mainItem.getTime()
mainItem.clicked(mainItem.selectedTime)
}
}
}
}

View file

@ -155,7 +155,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: Utils.exactDate(new Date(section)).toLocaleDateString(App.locale)
text: UtilsCpp.toDateString(section)
}
}
}

View file

@ -102,7 +102,7 @@ Rectangle {
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: Utils.exactDate(new Date(section)).toLocaleDateString(App.locale)
text: UtilsCpp.toDateString(section)
}
}
}

View file

@ -492,13 +492,6 @@ function fromUTC(date){
date.getUTCDate(), date.getUTCHours(),
date.getUTCMinutes(), date.getUTCSeconds()));
}
// return EXACTLY what date has been set (not take account of Locale timezones, eg. Date(2000,0,1) will print january and not december if timezone lead there.)
// Use this function for toLocaleString/toLocaleDateString or other
function exactDate(date) {
var timeOffset = date.getTimezoneOffset() * 60000 // getTimezoneOffset == UTC - locale
var exactDate = new Date(date.valueOf() + timeOffset) // Revert back JS timezone to get initial UTC
return exactDate
}
// -----------------------------------------------------------------------------
function formatSize (size) {

View file

@ -21,7 +21,7 @@ DialogPlus {
property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{}
onConferenceInfoModelChanged: {
dateField.setDate(conferenceManager.conferenceInfoModel.dateTime);
timeField.setTime(conferenceManager.conferenceInfoModel.dateTime);
timeField.setTime(UtilsCpp.toTimeString(conferenceManager.conferenceInfoModel.dateTime , 'hh:mm'));
selectedParticipants.setAddresses(conferenceInfoModel)
}
property bool forceSchedule : false
@ -130,18 +130,15 @@ DialogPlus {
}
conferenceManager.creationState = 1
if( scheduledSwitch.checked){
var startDateTime = Utils.buildDate(dateField.getDate(), timeField.getTime())
conferenceInfoModel.isScheduled = true
startDateTime.setSeconds(0)
conferenceInfoModel.timeZoneModel = timeZoneField.model.getAt(timeZoneField.currentIndex)
conferenceInfoModel.dateTime = startDateTime
conferenceInfoModel.setDateTime(dateField.getDate(), timeField.getTime()+':00', timeZoneField.model.getAt(timeZoneField.currentIndex) )
conferenceInfoModel.duration = durationField.model[durationField.currentIndex].value
}else
}else{
conferenceInfoModel.isScheduled = false
conferenceInfoModel.initDateTime()
}
conferenceInfoModel.subject = subject.text
conferenceInfoModel.description = description.text
conferenceInfoModel.setParticipants(selectedParticipants.participantListModel)
conferenceInfoModel.inviteMode = getInviteMode();
conferenceInfoModel.createConference(false && secureSwitch.checked) // TODO remove false when Encryption is ready to use
@ -247,8 +244,10 @@ DialogPlus {
checked: conferenceInfoModel.isScheduled
onClicked: {
if( !conferenceManager.forceSchedule)
checked = !checked
if( !conferenceManager.forceSchedule){
conferenceInfoModel.isScheduled = !checked
conferenceInfoModel.initDateTime()
}
}
indicatorStyle: SwitchStyle.aux
}
@ -270,8 +269,6 @@ DialogPlus {
Layout.fillHeight: true
Layout.margins: 10
columns: 4
property var locale: App.locale
property date currentDate: new Date()
property int cellWidth: (parent.width-15-20)/columns
@ -293,15 +290,13 @@ DialogPlus {
Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize }
TextField{id: dateField; Layout.preferredWidth: parent.cellWidth
color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize
property date currentDate: new Date()
function getDate(){
return currentDate
return text
}
function setDate(date){
currentDate = date
text = Utils.exactDate(date).toLocaleDateString(scheduleForm.locale, Qt.ISODate)
text = UtilsCpp.toDateString(date, 'yyyy/MM/dd')
}
text: conferenceManager.conferenceInfoModel ? Utils.exactDate(conferenceManager.conferenceInfoModel.dateTime).toLocaleDateString(scheduleForm.locale, Qt.ISODate) : ''
text: conferenceManager.conferenceInfoModel ? UtilsCpp.toDateString(conferenceManager.conferenceInfoModel.dateTime, 'yyyy/MM/dd') : ''
icon: 'drop_down_custom'
MouseArea{
anchors.fill: parent
@ -319,15 +314,12 @@ DialogPlus {
TextField{id: timeField; Layout.preferredWidth: parent.cellWidth
color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize
function getTime(){
return Date.fromLocaleTimeString(scheduleForm.locale, timeField._text, 'hh:mm')
return timeField.text
}
function setTime(date){
_text = date.toLocaleTimeString(scheduleForm.locale, 'hh:mm')
text = UtilsCpp.toTimeString(date, 'hh:mm')// Display the unchanged time
function setTime(time){
text = time
}
// hidden time to be used from JS : JS Local time can be wrong on Windows because of daylights that are not takken account.
property string _text: conferenceManager.conferenceInfoModel? conferenceManager.conferenceInfoModel.dateTime.toLocaleTimeString(scheduleForm.locale, 'hh:mm') : ''
text: conferenceManager.conferenceInfoModel? UtilsCpp.toTimeString(conferenceManager.conferenceInfoModel.dateTimeUtc, 'hh:mm') : ''
text: conferenceManager.conferenceInfoModel? UtilsCpp.toTimeString(conferenceManager.conferenceInfoModel.dateTime , 'hh:mm') : ''
icon: 'drop_down_custom'
onEditingFinished: if(rightStackView.currentItemType === 2) {
@ -341,9 +333,9 @@ DialogPlus {
anchors.right: parent.right
width: parent.width-50
onClicked: {
window.attachVirtualWindow(Utils.buildCommonDialogUri('DateTimeDialog'), {showTimePicker:true, selectedTime: new Date(timeField.getTime())}
window.attachVirtualWindow(Utils.buildCommonDialogUri('DateTimeDialog'), {showTimePicker:true, selectedTime: timeField.getTime()}
, function (status) {
if(status){
if(status && status.selectedTime){
timeField.setTime(status.selectedTime)
}
}
@ -374,31 +366,6 @@ DialogPlus {
selectionWidth: 500
rootItem: conferenceManager
}
function updateDateTime(){
var storedDate
if( dateField.text != '' && timeField.text != ''){
storedDate = Utils.buildDate(dateField.getDate(), timeField.getTime() )
}else
storedDate = new Date()
var currentDate = new Date()
if(currentDate >= storedDate){
var nextStoredDate = UtilsCpp.addMinutes(new Date(), 1)
dateField.setDate(nextStoredDate)
timeField.setTime(nextStoredDate)
if(rightStackView.currentItemType === 2) rightStackView.currentItem.selectedTime = nextStoredDate
}
}
Timer{
running: scheduleForm.visible && conferenceManager.isNew
repeat: true
interval: 1000
triggeredOnStart: true
onTriggered: {
if(conferenceManager.isNew)
scheduleForm.updateDateTime()
}
}
}
ColumnLayout {

View file

@ -141,7 +141,7 @@ Item{
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: Utils.exactDate(new Date(section)).toLocaleDateString(App.locale)
text: UtilsCpp.toDateString(section)
}
}
}