M image/system_a/data/lang/Deutsch.json => image/system_a/data/lang/Deutsch.json +3 -1
@@ 716,5 716,7 @@
"tethering_enable_question": "<text>Sie sind mit dem Computer verbunden.<br />Tethering einschalten?<br /><text color='4'>(einige Funktionen k\u00f6nnen deaktiviert sein)</text></text>",
"tethering_menu_access_decline": "<text>Tethering ist eingeschaltet.<br /><br />Schalten Sie Tethering aus,<br />um auf das Men\u00fc zuzugreifen</text>",
"tethering_phone_mode_change_prohibited": "<text>Tethering ist eingeschaltet.<br /><br />Andere Betriebsarten (Verbunden, DND,<br />Offline) werden von dieser Betriebsart \u00fcberlagert<br />und funktionieren nicht.</text>",
- "tethering_turn_off_question": "Tethering ausschalten?"
+ "tethering_turn_off_question": "Tethering ausschalten?",
+ "unsaved_changes": "Ungespeicherte \u00c4nderungen",
+ "exit_without_saving": "M\u00f6chten Sie den Vorgang ohne Speichern beenden?"
}
M image/system_a/data/lang/English.json => image/system_a/data/lang/English.json +3 -1
@@ 727,5 727,7 @@
"tethering_enable_question": "<text>You're connected to the computer.<br />Turn tethering on?<br /><text color='4'>(some functions may be disabled)</text></text>",
"tethering_menu_access_decline": "<text>Tethering is on.<br /><br />To access menu,<br />turn tethering off.</text>",
"tethering_phone_mode_change_prohibited": "<text>Tethering is on.<br /><br />Other modes (Connected, DND,<br />Offline) are overriden by this mode<br />and are not working.</text>",
- "tethering_turn_off_question": "Turn tethering off?"
+ "tethering_turn_off_question": "Turn tethering off?",
+ "unsaved_changes": "Unsaved changes",
+ "exit_without_saving": "<text>Do you want to exit </text><br></br><text weight='bold'>without saving?</text>"
}
M image/system_a/data/lang/Espanol.json => image/system_a/data/lang/Espanol.json +3 -1
@@ 716,5 716,7 @@
"tethering_enable_question": "<text>Est\u00e1s conectado al ordenador.<br />\u00bfActivar el anclaje de red?<br /><text color='4'>(algunas funciones podr\u00edan desactivarse)</text></text>",
"tethering_menu_access_decline": "<text>El anclaje de red est\u00e1 activado.<br /><br />Para acceder al men\u00fa,<br />desactiva el tethering.</text>",
"tethering_phone_mode_change_prohibited": "<text>El anclaje de red est\u00e1 activado.<br /><br />Este modo anula otros modos (Conectado, No molestar,<br />Desconectado) <br />y hace que dejen de funcionar.</text>",
- "tethering_turn_off_question": "\u00bfDesactivar el anclaje de red?"
+ "tethering_turn_off_question": "\u00bfDesactivar el anclaje de red?",
+ "unsaved_changes": "Cambios no guardados",
+ "exit_without_saving": "\u00bfQuieres salir sin guardar?"
}
M image/system_a/data/lang/Francais.json => image/system_a/data/lang/Francais.json +3 -1
@@ 687,5 687,7 @@
"tethering_enable_question": "<text>Vous \u00eates connect\u00e9 \u00e0 l'ordinateur.<br />Voulez-vous activer le partage de connexion?<br /><text color='4'>(certaines fonctions peuvent \u00eatre d\u00e9sactiv\u00e9es)</text></text>",
"tethering_menu_access_decline": "<text>Le partage de connexion est activ\u00e9.<br /><br />Pour acc\u00e9der au menu, veuillez<br />d\u00e9sactiver le partage de connextion.</text>",
"tethering_phone_mode_change_prohibited": "<text>Le partage de connexion est activ\u00e9.<br /><br />Ce mode-ci remplace et d\u00e9sactive les autres modes<br />(Connect\u00e9, NPD, Hors ligne)</text>",
- "tethering_turn_off_question": "Voulez-vous d\u00e9sactiver le partage de connexion?"
+ "tethering_turn_off_question": "Voulez-vous d\u00e9sactiver le partage de connexion?",
+ "unsaved_changes": "Modifications non enregistr\u00e9es",
+ "exit_without_saving": "Voulez-vous quitter sans sauvegarder ?"
}
M image/system_a/data/lang/Polski.json => image/system_a/data/lang/Polski.json +3 -1
@@ 715,5 715,7 @@
"tethering_enable_question": "<text>Po\u0142\u0105czono z komputerem.<br />W\u0142\u0105czy\u0107 tethering?<br /><text color='4'>(Niekt\u00f3re funkcje mog\u0105 by\u0107 niedost\u0119pne)</text></text>",
"tethering_menu_access_decline": "<text>Tethering w\u0142\u0105czony.<br /><br />Aby przej\u015b\u0107 do menu,<br />wy\u0142\u0105cz tethering.</text>",
"tethering_phone_mode_change_prohibited": "<text>Tethering w\u0142\u0105czony.<br /><br />Ten tryb powoduje, \u017ce inne tryby (Po\u0142\u0105czony, Nie przeszkadza\u0107,<br />Offline) nie dzia\u0142aj\u0105.</text>",
- "tethering_turn_off_question": "Wy\u0142\u0105czy\u0107 tethering?"
+ "tethering_turn_off_question": "Wy\u0142\u0105czy\u0107 tethering?",
+ "unsaved_changes": "Niezapisane zmiany",
+ "exit_without_saving": "<text>Czy chcesz wyj\u015b\u0107 </text><br></br><text weight='bold'>bez zapisywania?</text>"
}
M image/system_a/data/lang/Svenska.json => image/system_a/data/lang/Svenska.json +3 -1
@@ 526,5 526,7 @@
"tethering_menu_access_decline": "<text>Internetdelning \u00e4r p\u00e5.<br /><br />F\u00f6r att \u00f6ppna menyn, <br />st\u00e4ng av internetdelning.</text>",
"tethering_phone_mode_change_prohibited": "<text>Internetdelning \u00e4r p\u00e5.<br /><br />Andra l\u00e4gen (ansluten, DND, <br />offline) \u00e5sidos\u00e4tts av detta <br /> l\u00e4ge och fungerar inte.</text>",
"tethering_turn_off_question": "St\u00e4nga av Internetdelning?",
- "volume_text": "LJUDVOLYM"
+ "volume_text": "LJUDVOLYM",
+ "unsaved_changes": "Osparade \u00e4ndringar",
+ "exit_without_saving": "Vill du g\u00e5 h\u00e4rifr\u00e5n utan att spara?"
}
M module-apps/application-notes/ApplicationNotes.cpp => module-apps/application-notes/ApplicationNotes.cpp +4 -0
@@ 124,6 124,10 @@ namespace app
windowsFactory.attach(window::name::option_window, [](ApplicationCommon *app, const std::string &name) {
return std::make_unique<gui::OptionWindow>(app, name);
});
+ windowsFactory.attach(gui::name::window::notes_dialog_yes_no,
+ [](ApplicationCommon *app, const std::string &name) {
+ return std::make_unique<gui::DialogYesNo>(app, name);
+ });
attachPopups({gui::popup::ID::Volume,
gui::popup::ID::Tethering,
M module-apps/application-notes/include/application-notes/ApplicationNotes.hpp => module-apps/application-notes/include/application-notes/ApplicationNotes.hpp +2 -1
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 14,6 14,7 @@ namespace gui::name::window
inline constexpr auto notes_search_result = "NotesSearchResult";
inline constexpr auto note_dialog = "Dialog";
inline constexpr auto note_confirm_dialog = "ConfirmDialog";
+ inline constexpr auto notes_dialog_yes_no = "DialogYesNo";
} // namespace gui::name::window
namespace app
M module-apps/application-notes/windows/NoteEditWindow.cpp => module-apps/application-notes/windows/NoteEditWindow.cpp +24 -0
@@ 2,6 2,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "NoteEditWindow.hpp"
+#include "DialogMetadataMessage.hpp"
#include <sstream>
@@ 155,6 156,24 @@ namespace app::notes
std::make_unique<gui::OptionsWindowOptions>(noteEditOptions(application, edit)));
}
}
+ if (isCurrentTextDifferentThanSaved() &&
+ (inputEvent.isShortRelease(gui::KeyCode::KEY_RF) || inputEvent.isLongRelease(gui::KeyCode::KEY_RF))) {
+
+ // Show a popup warning about possible data loss
+ auto metaData = std::make_unique<gui::DialogMetadataMessage>(
+ gui::DialogMetadata{utils::translate("unsaved_changes"),
+ "delete_128px_W_G",
+ utils::translate("exit_without_saving"),
+ "",
+ [=]() -> bool {
+ application->returnToPreviousWindow(); // To exit this popup
+ application->returnToPreviousWindow(); // To switch back to previous window
+ return true;
+ }});
+
+ application->switchWindow(gui::name::window::notes_dialog_yes_no, std::move(metaData));
+ return true;
+ }
return AppWindow::onInput(inputEvent);
}
@@ 169,4 188,9 @@ namespace app::notes
{
return (edit != nullptr) ? edit->isEmpty() : true;
}
+
+ bool NoteEditWindow::isCurrentTextDifferentThanSaved()
+ {
+ return notesRecord->snippet != edit->getText();
+ }
} // namespace app::notes
M module-apps/application-notes/windows/NoteEditWindow.hpp => module-apps/application-notes/windows/NoteEditWindow.hpp +1 -0
@@ 42,6 42,7 @@ namespace app::notes
void setCharactersCount(std::uint32_t count);
void setNoteText(const UTF8 &text);
void saveNote();
+ bool isCurrentTextDifferentThanSaved();
std::unique_ptr<NoteEditWindowContract::Presenter> presenter;
std::shared_ptr<NotesRecord> notesRecord;
M module-apps/application-phonebook/models/NewContactModel.cpp => module-apps/application-phonebook/models/NewContactModel.cpp +11 -0
@@ 235,3 235,14 @@ void NewContactModel::openTextOptions(gui::Text *text)
auto data = std::make_unique<PhonebookInputOptionData>(text);
application->switchWindow(gui::window::name::input_options, std::move(data));
}
+bool NewContactModel::isAnyUnsavedChange(std::shared_ptr<ContactRecord> contactRecord)
+{
+ for (const auto &item : internalData) {
+ if (item->onCheckUnsavedChangeCallback) {
+ if (item->onCheckUnsavedChangeCallback(contactRecord)) {
+ return true;
+ }
+ }
+ }
+ return false; // there is no change between already provided data and saved ones
+}
M module-apps/application-phonebook/models/NewContactModel.hpp => module-apps/application-phonebook/models/NewContactModel.hpp +1 -0
@@ 28,6 28,7 @@ class NewContactModel : public app::InternalModel<gui::ContactListItem *>, publi
void createData();
bool verifyData();
bool emptyData();
+ bool isAnyUnsavedChange(std::shared_ptr<ContactRecord> contactRecord);
[[nodiscard]] auto getRequestType() -> PhonebookItemData::RequestType;
[[nodiscard]] auto requestRecordsCount() -> unsigned int override;
M module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.cpp => module-apps/application-phonebook/widgets/InputBoxWithLabelAndIconWidget.cpp +7 -1
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "InputBoxWithLabelAndIconWidget.hpp"
@@ 172,6 172,9 @@ namespace gui
onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) {
tickImage->visible = contact->isOnFavourites();
};
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ return tickImage->visible != contact->isOnFavourites();
+ };
}
void InputBoxWithLabelAndIconWidget::addToICEHandler()
{
@@ 214,6 217,9 @@ namespace gui
};
onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) { contact->addToIce(tickImage->visible); };
onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { tickImage->visible = contact->isOnIce(); };
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ return tickImage->visible != contact->isOnIce();
+ };
}
} /* namespace gui */
M module-apps/application-phonebook/widgets/InputLinesWithLabelWidget.cpp => module-apps/application-phonebook/widgets/InputLinesWithLabelWidget.cpp +30 -0
@@ 163,6 163,10 @@ namespace gui
onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) { contact->primaryName = inputText->getText(); };
onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->primaryName); };
onEmptyCallback = [&]() { return inputText->isEmpty(); };
+
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ return contact->primaryName != inputText->getText();
+ };
}
void InputLinesWithLabelWidget::secondNameHandler()
{
@@ 176,6 180,10 @@ namespace gui
};
onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->alternativeName); };
onEmptyCallback = [&]() { return inputText->isEmpty(); };
+
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ return contact->alternativeName != inputText->getText();
+ };
}
void InputLinesWithLabelWidget::numberHandler()
{
@@ 208,6 216,11 @@ namespace gui
};
onEmptyCallback = [&]() { return inputText->isEmpty(); };
+
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ std::string savedNumber = (!contact->numbers.empty()) ? (contact->numbers[0].number.getEntered()) : "";
+ return savedNumber != std::string(inputText->getText());
+ };
}
void InputLinesWithLabelWidget::secondNumberHandler()
{
@@ 241,6 254,11 @@ namespace gui
};
onEmptyCallback = [&]() { return inputText->isEmpty(); };
+
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ std::string savedNumber = (contact->numbers.size() > 1) ? (contact->numbers[1].number.getEntered()) : "";
+ return savedNumber != std::string(inputText->getText());
+ };
}
void InputLinesWithLabelWidget::emailHandler()
{
@@ 252,6 270,10 @@ namespace gui
onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) { contact->mail = inputText->getText(); };
onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->mail); };
onEmptyCallback = [&]() { return inputText->isEmpty(); };
+
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ return contact->mail != inputText->getText();
+ };
}
void InputLinesWithLabelWidget::addressHandler()
{
@@ 262,6 284,10 @@ namespace gui
onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) { contact->address = inputText->getText(); };
onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->address); };
+
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ return contact->address != inputText->getText();
+ };
}
void InputLinesWithLabelWidget::noteHandler()
{
@@ 272,6 298,10 @@ namespace gui
onSaveCallback = [&](std::shared_ptr<ContactRecord> contact) { contact->note = inputText->getText(); };
onLoadCallback = [&](std::shared_ptr<ContactRecord> contact) { inputText->setText(contact->note); };
+
+ onCheckUnsavedChangeCallback = [&](std::shared_ptr<ContactRecord> contact) {
+ return contact->note != inputText->getText();
+ };
}
} /* namespace gui */
M module-apps/application-phonebook/windows/PhonebookNewContact.cpp => module-apps/application-phonebook/windows/PhonebookNewContact.cpp +73 -37
@@ 115,6 115,22 @@ namespace gui
return true;
}
+ void PhonebookNewContact::showDialogUnsavedChanges(std::function<bool()> whereToGoOnYes)
+ {
+ // Show a popup warning about possible data loss
+ auto metaData = std::make_unique<gui::DialogMetadataMessage>(
+ gui::DialogMetadata{utils::translate("unsaved_changes"),
+ "delete_128px_W_G",
+ utils::translate("exit_without_saving"),
+ "",
+ [=]() -> bool {
+ application->returnToPreviousWindow(); // To exit this popup
+ return whereToGoOnYes();
+ }});
+
+ application->switchWindow(gui::window::name::dialog_yes_no, std::move(metaData));
+ }
+
void PhonebookNewContact::setSaveButtonVisible(bool visible)
{
navBar->setActive(nav_bar::Side::Center, visible);
@@ 135,14 151,31 @@ namespace gui
return true;
}
else if (!inputEvent.isShortRelease(KeyCode::KEY_RF) || !shouldCurrentAppBeIgnoredOnSwitchBack()) {
+ if (areUnsavedChanges()) {
+ if (inputEvent.isShortRelease(gui::KeyCode::KEY_RF) || inputEvent.isLongRelease(gui::KeyCode::KEY_RF)) {
+ showDialogUnsavedChanges([this]() {
+ application->returnToPreviousWindow();
+ return true;
+ });
+ return true;
+ }
+ }
return AppWindow::onInput(inputEvent);
}
- return shouldCurrentAppBeIgnoredOnSwitchBack()
- ? app::manager::Controller::switchBack(
- application,
- std::make_unique<app::manager::SwitchBackRequest>(nameOfPreviousApplication.value()))
- : true;
+ auto returnWhenCurrentAppShouldBeIgnoredOnSwitchBack = [this]() {
+ return shouldCurrentAppBeIgnoredOnSwitchBack()
+ ? app::manager::Controller::switchBack(
+ application,
+ std::make_unique<app::manager::SwitchBackRequest>(nameOfPreviousApplication.value()))
+ : true;
+ };
+
+ if (areUnsavedChanges()) {
+ showDialogUnsavedChanges(returnWhenCurrentAppShouldBeIgnoredOnSwitchBack);
+ return true;
+ }
+ return returnWhenCurrentAppShouldBeIgnoredOnSwitchBack();
}
auto PhonebookNewContact::verifyAndSave() -> bool
@@ 207,38 240,37 @@ namespace gui
DBServiceAPI::MatchContactByPhoneNumber(application, duplicatedNumber, duplicatedNumberContactID);
const auto oldContactRecord = (matchedContact != nullptr) ? *matchedContact : ContactRecord{};
- auto metaData = std::make_unique<gui::DialogMetadataMessage>(
- gui::DialogMetadata{duplicatedNumber.getFormatted(),
- "info_128px_W_G",
- text::RichTextParser()
- .parse(utils::translate("app_phonebook_duplicate_numbers"),
- nullptr,
- gui::text::RichTextParser::TokenMap(
- {{"$CONTACT_FORMATTED_NAME$", oldContactRecord.getFormattedName()}}))
- ->getText(),
- "",
- [=]() -> bool {
- if (contactAction == ContactAction::Add) {
- contact->ID = oldContactRecord.ID;
- }
- if (!DBServiceAPI::ContactUpdate(application, *contact)) {
- LOG_ERROR("Contact id=%" PRIu32 " update failed", contact->ID);
- return false;
- }
-
- /* Pop "Add contact" window from the stack so that clicking
- * back button after saving the modified contact returns to
- * contacts list, not to the "Add contact" window. */
- application->popWindow(gui::window::name::new_contact);
-
- /* Switch to contact details */
- auto switchData =
- std::make_unique<PhonebookItemData>(contact, newContactModel->getRequestType());
- switchData->ignoreCurrentWindowOnStack = true;
- application->switchWindow(gui::window::name::contact, std::move(switchData));
-
- return true;
- }});
+ auto metaData = std::make_unique<gui::DialogMetadataMessage>(gui::DialogMetadata{
+ duplicatedNumber.getFormatted(),
+ "info_128px_W_G",
+ text::RichTextParser()
+ .parse(utils::translate("app_phonebook_duplicate_numbers"),
+ nullptr,
+ gui::text::RichTextParser::TokenMap(
+ {{"$CONTACT_FORMATTED_NAME$", oldContactRecord.getFormattedName()}}))
+ ->getText(),
+ "",
+ [=]() -> bool {
+ if (contactAction == ContactAction::Add) {
+ contact->ID = oldContactRecord.ID;
+ }
+ if (!DBServiceAPI::ContactUpdate(application, *contact)) {
+ LOG_ERROR("Contact id=%" PRIu32 " update failed", contact->ID);
+ return false;
+ }
+
+ /* Pop "Add contact" window from the stack so that clicking
+ * back button after saving the modified contact returns to
+ * contacts list, not to the "Add contact" window. */
+ application->popWindow(gui::window::name::new_contact);
+
+ /* Switch to contact details */
+ auto switchData = std::make_unique<PhonebookItemData>(contact, newContactModel->getRequestType());
+ switchData->ignoreCurrentWindowOnStack = true;
+ application->switchWindow(gui::window::name::contact, std::move(switchData));
+
+ return true;
+ }});
application->switchWindow(gui::window::name::dialog_yes_no, std::move(metaData));
}
@@ 301,5 333,9 @@ namespace gui
contactByID->front().primaryName.empty() and contactByID->front().alternativeName.empty() and
contactByID->front().numbers.empty();
}
+ bool PhonebookNewContact::areUnsavedChanges() const
+ {
+ return newContactModel->isAnyUnsavedChange(contact);
+ }
} // namespace gui
M module-apps/application-phonebook/windows/PhonebookNewContact.hpp => module-apps/application-phonebook/windows/PhonebookNewContact.hpp +2 -0
@@ 38,9 38,11 @@ namespace gui
void showDialogDuplicatedNumber(const utils::PhoneNumber::View &duplicatedNumber,
const std::uint32_t duplicatedNumberContactID = 0u);
void showDialogDuplicatedSpeedDialNumber();
+ void showDialogUnsavedChanges(std::function<bool()> whereToGoOnYes = nullptr);
void setSaveButtonVisible(bool visible);
void showContactDeletedNotification();
bool checkIfContactWasDeletedDuringEditProcess() const;
+ bool areUnsavedChanges() const;
std::shared_ptr<ContactRecord> contact = nullptr;
std::shared_ptr<NewContactModel> newContactModel = nullptr;
M module-gui/gui/widgets/ListItem.hpp => module-gui/gui/widgets/ListItem.hpp +7 -6
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 22,10 22,11 @@ namespace gui
class ListItemWithCallbacks : public ListItem
{
public:
- std::function<bool()> onEmptyCallback = nullptr;
- std::function<bool()> onContentChangedCallback = nullptr;
- std::function<bool(std::string &errorMessage)> onVerifyCallback = nullptr;
- std::function<void(std::shared_ptr<T> record)> onSaveCallback = nullptr;
- std::function<void(std::shared_ptr<T> record)> onLoadCallback = nullptr;
+ std::function<bool()> onEmptyCallback = nullptr;
+ std::function<bool()> onContentChangedCallback = nullptr;
+ std::function<bool(std::string &errorMessage)> onVerifyCallback = nullptr;
+ std::function<void(std::shared_ptr<T> record)> onSaveCallback = nullptr;
+ std::function<void(std::shared_ptr<T> record)> onLoadCallback = nullptr;
+ std::function<bool(std::shared_ptr<T> record)> onCheckUnsavedChangeCallback = nullptr;
};
} /* namespace gui */
M pure_changelog.md => pure_changelog.md +1 -0
@@ 37,6 37,7 @@
* Fixed missing tethering icon on "Tethering is on" window
* Fixed showing "Copy text" option in empty note
* Fixed "Copy" option missing from the options list in "New message" window
+* Fixed losing unsaved data on go back
## [1.7.2 2023-07-28]