~aleteoryx/muditaos

5b06da46a6dabf26d5bd37ffdbcc4ec63e57393f — rrandomsky 2 years ago 3cbbeff
[MOS-948] Fix for tethering popup was losing user unsaved data

Fixed data loss when creating or editing a note/contact after connecting
the USB cable and selecting the option to enable tetering in the popup window
that appears. Now, when the user wants to enable tethering, they will be asked
if they want to exit without saving.
M module-apps/application-notes/windows/NoteEditWindow.cpp => module-apps/application-notes/windows/NoteEditWindow.cpp +2 -2
@@ 156,7 156,7 @@ namespace app::notes
                    std::make_unique<gui::OptionsWindowOptions>(noteEditOptions(application, edit)));
            }
        }
        if (isCurrentTextDifferentThanSaved() &&
        if (isAnyUnsavedUserDataInWindow() &&
            (inputEvent.isShortRelease(gui::KeyCode::KEY_RF) || inputEvent.isLongRelease(gui::KeyCode::KEY_RF))) {

            // Show a popup warning about possible data loss


@@ 189,7 189,7 @@ namespace app::notes
        return (edit != nullptr) ? edit->isEmpty() : true;
    }

    bool NoteEditWindow::isCurrentTextDifferentThanSaved()
    bool NoteEditWindow::isAnyUnsavedUserDataInWindow() const
    {
        return notesRecord->snippet != edit->getText();
    }

M module-apps/application-notes/windows/NoteEditWindow.hpp => module-apps/application-notes/windows/NoteEditWindow.hpp +1 -1
@@ 42,7 42,7 @@ namespace app::notes
        void setCharactersCount(std::uint32_t count);
        void setNoteText(const UTF8 &text);
        void saveNote();
        bool isCurrentTextDifferentThanSaved();
        bool isAnyUnsavedUserDataInWindow() const override;

        std::unique_ptr<NoteEditWindowContract::Presenter> presenter;
        std::shared_ptr<NotesRecord> notesRecord;

M module-apps/application-phonebook/windows/PhonebookNewContact.cpp => module-apps/application-phonebook/windows/PhonebookNewContact.cpp +4 -3
@@ 151,7 151,7 @@ namespace gui
            return true;
        }
        else if (!inputEvent.isShortRelease(KeyCode::KEY_RF) || !shouldCurrentAppBeIgnoredOnSwitchBack()) {
            if (areUnsavedChanges()) {
            if (isAnyUnsavedUserDataInWindow()) {
                if (inputEvent.isShortRelease(gui::KeyCode::KEY_RF) || inputEvent.isLongRelease(gui::KeyCode::KEY_RF)) {
                    showDialogUnsavedChanges([this]() {
                        application->returnToPreviousWindow();


@@ 171,7 171,7 @@ namespace gui
                       : true;
        };

        if (areUnsavedChanges()) {
        if (isAnyUnsavedUserDataInWindow()) {
            showDialogUnsavedChanges(returnWhenCurrentAppShouldBeIgnoredOnSwitchBack);
            return true;
        }


@@ 336,7 336,8 @@ namespace gui
               contactByID->front().primaryName.empty() and contactByID->front().alternativeName.empty() and
               contactByID->front().numbers.empty();
    }
    bool PhonebookNewContact::areUnsavedChanges() const

    bool PhonebookNewContact::isAnyUnsavedUserDataInWindow() const
    {
        return newContactModel->isAnyUnsavedChange(contact);
    }

M module-apps/application-phonebook/windows/PhonebookNewContact.hpp => module-apps/application-phonebook/windows/PhonebookNewContact.hpp +1 -1
@@ 43,7 43,7 @@ namespace gui
        void setSaveButtonVisible(bool visible);
        void showContactDeletedNotification();
        bool checkIfContactWasDeletedDuringEditProcess() const;
        bool areUnsavedChanges() const;
        bool isAnyUnsavedUserDataInWindow() const override;

        std::shared_ptr<ContactRecord> contact           = nullptr;
        std::shared_ptr<NewContactModel> newContactModel = nullptr;

M module-apps/apps-common/ApplicationCommon.cpp => module-apps/apps-common/ApplicationCommon.cpp +5 -0
@@ 1015,6 1015,11 @@ namespace app
        return window;
    }

    std::size_t ApplicationCommon::getSizeOfWindowsStack()
    {
        return windowsStack().getSize();
    }

    bool ApplicationCommon::isCurrentWindow(const std::string &windowName) const noexcept
    {
        if (const auto &window = windowsStack().get(topWindow); window.has_value()) {

M module-apps/apps-common/ApplicationCommon.hpp => module-apps/apps-common/ApplicationCommon.hpp +3 -0
@@ 379,6 379,9 @@ namespace app
        /// if there is none - returns default window
        /// @ingrup AppWindowStack
        gui::AppWindow *getCurrentWindow();
        /// getter for size of windows stack
        /// @ingrup AppWindowStack
        std::size_t getSizeOfWindowsStack();
        /// @ingrup AppWindowStack
        bool isCurrentWindow(const std::string &windowName) const noexcept;
        bool isPreviousWindow(const std::string &windowName) const noexcept;

M module-apps/apps-common/WindowsStack.cpp => module-apps/apps-common/WindowsStack.cpp +5 -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

#include "WindowsStack.hpp"


@@ 135,5 135,9 @@ namespace app
            std::advance(it, 1);
        }
    }
    std::size_t WindowsStack::getSize() const noexcept
    {
        return stack.size();
    }

} // namespace app

M module-apps/apps-common/WindowsStack.hpp => module-apps/apps-common/WindowsStack.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


@@ 51,6 51,7 @@ namespace app
        std::map<std::string, std::unique_ptr<gui::AppWindow>>::const_iterator begin() const;
        std::map<std::string, std::unique_ptr<gui::AppWindow>>::const_iterator end() const;
        [[nodiscard]] bool isEmpty() const noexcept;
        [[nodiscard]] std::size_t getSize() const noexcept;

        /// add window on top of stack
        void push(const std::string &name,

M module-apps/apps-common/popups/TetheringConfirmationPopup.cpp => module-apps/apps-common/popups/TetheringConfirmationPopup.cpp +41 -3
@@ 21,12 21,50 @@ namespace gui
        metadata.text   = utils::translate("tethering_enable_question");
        metadata.icon   = "tethering_128px_W_G";
        metadata.action = [this]() {
            application->bus.sendUnicast(std::make_shared<sys::TetheringEnabledResponse>(),
                                         service::name::system_manager);
            app::manager::Controller::sendAction(application, app::manager::actions::Home);
            // check if there is any unsaved data in any window in stack
            bool IsDataUnsaved   = false;
            auto windowStackSize = application->getSizeOfWindowsStack();
            for (auto windowOnStackNumber = windowStackSize; windowOnStackNumber > 0; windowOnStackNumber--) {
                if (const auto &previousWindowName = application->getPreviousWindow(windowOnStackNumber);
                    previousWindowName.has_value()) {
                    const auto previousWindow = application->getWindow(previousWindowName.value());
                    IsDataUnsaved             = previousWindow->isAnyUnsavedUserDataInWindow() ? true : IsDataUnsaved;
                }
            }

            // what is the expected action when YES is selected
            auto onYesAction = [this]() {
                application->bus.sendUnicast(std::make_shared<sys::TetheringEnabledResponse>(),
                                             service::name::system_manager);
                app::manager::Controller::sendAction(application, app::manager::actions::Home);
                return true;
            };

            // if there are any unsaved changes, open the appropriate popup, otherwise proceed as usual
            if (IsDataUnsaved) {
                showDialogUnsavedChanges(onYesAction);
                return true;
            }
            onYesAction();
            return true;
        };
        auto msg = std::make_unique<DialogMetadataMessage>(std::move(metadata));
        DialogYesNo::onBeforeShow(mode, msg.get());
    }

    void TetheringConfirmationPopup::showDialogUnsavedChanges(std::function<bool()> whatToDoOnYes)
    {
        // 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 whatToDoOnYes();
                                }});

        application->switchWindow(gui::window::name::dialog_yes_no, std::move(metaData));
    }
} // namespace gui

M module-apps/apps-common/popups/TetheringConfirmationPopup.hpp => module-apps/apps-common/popups/TetheringConfirmationPopup.hpp +8 -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

#pragma once


@@ 6,6 6,10 @@
#include <string>

#include "Dialog.hpp"
namespace gui::window::name
{
    inline constexpr auto dialog_yes_no = "DialogYesNo";
} // namespace gui::window::name

namespace gui
{


@@ 15,5 19,8 @@ namespace gui
        TetheringConfirmationPopup(app::ApplicationCommon *app, const std::string &name);

        void onBeforeShow(ShowMode mode, SwitchData *data) override;

      private:
        void showDialogUnsavedChanges(std::function<bool()> whatToDoOnYes);
    };
}; // namespace gui

M module-gui/gui/widgets/Window.hpp => module-gui/gui/widgets/Window.hpp +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

#pragma once


@@ 89,6 89,12 @@ namespace gui
        {
            return name;
        }

        /// used for get info about any unsaved user data changes
        virtual bool isAnyUnsavedUserDataInWindow() const
        {
            return false;
        }
    };

} /* namespace gui */

M pure_changelog.md => pure_changelog.md +1 -0
@@ 44,6 44,7 @@
* Fixed the ability to create a contact with the same primary and secondary phone number, which resulted in mismatching
* Fixed losing drafted message and recipient number in new message windows
* Fixed misleading "Save" button behavior in "Date and time" window
* Fixed losing unsaved user data when tethering is switching on

## [1.7.2 2023-07-28]