~aleteoryx/muditaos

0e42b0016ee3e901ed2f27e71a6dba17035276af — Wojtek Rzepecki 4 years ago 2eca1d9
[EGD-7044] Strip down calendar code

Stripping the calendar code to provide only
MVP defined functionalities
129 files changed, 85 insertions(+), 9257 deletions(-)

D art/phone/home_notifications/calendar_notification_icon.png
D image/assets/images/calendar_notification_icon.vpi
M image/assets/lang/English.json
M image/assets/lang/Svenska.json
M module-apps/application-alarm-clock/windows/CustomRepeatWindow.cpp
M module-apps/application-calendar/ApplicationCalendar.cpp
M module-apps/application-calendar/ApplicationCalendar.hpp
M module-apps/application-calendar/CMakeLists.txt
M module-apps/application-calendar/data/CalendarData.hpp
D module-apps/application-calendar/models/AllEventsModel.cpp
D module-apps/application-calendar/models/AllEventsModel.hpp
D module-apps/application-calendar/models/CustomRepeatModel.cpp
D module-apps/application-calendar/models/CustomRepeatModel.hpp
D module-apps/application-calendar/models/DayEventsModel.cpp
D module-apps/application-calendar/models/DayEventsModel.hpp
D module-apps/application-calendar/models/EventDetailModel.cpp
D module-apps/application-calendar/models/EventDetailModel.hpp
M module-apps/application-calendar/models/MonthModel.cpp
M module-apps/application-calendar/models/MonthModel.hpp
D module-apps/application-calendar/models/NewEditEventModel.cpp
D module-apps/application-calendar/models/NewEditEventModel.hpp
D module-apps/application-calendar/widgets/AllEventsItem.cpp
D module-apps/application-calendar/widgets/AllEventsItem.hpp
D module-apps/application-calendar/widgets/CalendarDateItem.cpp
D module-apps/application-calendar/widgets/CalendarDateItem.hpp
D module-apps/application-calendar/widgets/CalendarListItem.hpp
D module-apps/application-calendar/widgets/CalendarListView.cpp
D module-apps/application-calendar/widgets/CalendarListView.hpp
M module-apps/application-calendar/widgets/CalendarStyle.hpp
D module-apps/application-calendar/widgets/CalendarTimeItem.cpp
D module-apps/application-calendar/widgets/CalendarTimeItem.hpp
D module-apps/application-calendar/widgets/CheckBoxWithLabelItem.cpp
D module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp
D module-apps/application-calendar/widgets/DayEventsItem.cpp
D module-apps/application-calendar/widgets/DayEventsItem.hpp
M module-apps/application-calendar/widgets/DayLabel.cpp
D module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp
D module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp
M module-apps/application-calendar/widgets/MonthBox.cpp
D module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.cpp
D module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.hpp
D module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp
D module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp
D module-apps/application-calendar/widgets/SeveralOptionsItem.cpp
D module-apps/application-calendar/widgets/SeveralOptionsItem.hpp
D module-apps/application-calendar/widgets/TextWithLabelItem.cpp
D module-apps/application-calendar/widgets/TextWithLabelItem.hpp
D module-apps/application-calendar/windows/AllEventsWindow.cpp
D module-apps/application-calendar/windows/AllEventsWindow.hpp
D module-apps/application-calendar/windows/CalendarEventsOptionsWindow.cpp
D module-apps/application-calendar/windows/CalendarEventsOptionsWindow.hpp
M module-apps/application-calendar/windows/CalendarMainWindow.cpp
M module-apps/application-calendar/windows/CalendarMainWindow.hpp
D module-apps/application-calendar/windows/CustomRepeatWindow.cpp
D module-apps/application-calendar/windows/CustomRepeatWindow.hpp
D module-apps/application-calendar/windows/DayEventsWindow.cpp
D module-apps/application-calendar/windows/DayEventsWindow.hpp
D module-apps/application-calendar/windows/EventDetailWindow.cpp
D module-apps/application-calendar/windows/EventDetailWindow.hpp
D module-apps/application-calendar/windows/EventReminderWindow.cpp
D module-apps/application-calendar/windows/EventReminderWindow.hpp
D module-apps/application-calendar/windows/NewEditEventWindow.cpp
D module-apps/application-calendar/windows/NewEditEventWindow.hpp
M module-apps/application-desktop/windows/DesktopMainWindow.cpp
M module-apps/application-settings-new/ApplicationSettings.cpp
M module-apps/application-settings-new/ApplicationSettings.hpp
M module-apps/application-settings-new/CMakeLists.txt
M module-apps/application-settings-new/windows/AppsAndToolsWindow.cpp
D module-apps/application-settings-new/windows/CalendarWindow.cpp
D module-apps/application-settings-new/windows/CalendarWindow.hpp
M module-apps/apps-common/CMakeLists.txt
M module-apps/apps-common/popups/lock-popups/PhoneLockedWindow.cpp
D module-apps/apps-common/windows/NoEvents.cpp
D module-apps/apps-common/windows/NoEvents.hpp
M module-db/CMakeLists.txt
D module-db/Databases/EventsDB.cpp
D module-db/Databases/EventsDB.hpp
M module-db/Interface/BaseInterface.hpp
D module-db/Interface/EventsRecord.cpp
D module-db/Interface/EventsRecord.hpp
D module-db/Tables/EventsTable.cpp
D module-db/Tables/EventsTable.hpp
D module-db/queries/calendar/QueryEventsAdd.cpp
D module-db/queries/calendar/QueryEventsAdd.hpp
D module-db/queries/calendar/QueryEventsEdit.cpp
D module-db/queries/calendar/QueryEventsEdit.hpp
D module-db/queries/calendar/QueryEventsEditICS.cpp
D module-db/queries/calendar/QueryEventsEditICS.hpp
D module-db/queries/calendar/QueryEventsGet.cpp
D module-db/queries/calendar/QueryEventsGet.hpp
D module-db/queries/calendar/QueryEventsGetAll.cpp
D module-db/queries/calendar/QueryEventsGetAll.hpp
D module-db/queries/calendar/QueryEventsGetAllLimited.cpp
D module-db/queries/calendar/QueryEventsGetAllLimited.hpp
D module-db/queries/calendar/QueryEventsGetFiltered.cpp
D module-db/queries/calendar/QueryEventsGetFiltered.hpp
D module-db/queries/calendar/QueryEventsGetFilteredByDay.cpp
D module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp
D module-db/queries/calendar/QueryEventsRemove.cpp
D module-db/queries/calendar/QueryEventsRemove.hpp
D module-db/queries/calendar/QueryEventsRemoveICS.cpp
D module-db/queries/calendar/QueryEventsRemoveICS.hpp
D module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp
D module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp
M module-db/tests/CMakeLists.txt
D module-db/tests/EventsRecord_tests.cpp
D module-db/tests/EventsTable_tests.cpp
M module-services/service-appmgr/service-appmgr/Actions.hpp
M module-services/service-db/ServiceDB.cpp
M module-services/service-db/ServiceDB.hpp
M module-services/service-desktop/CMakeLists.txt
M module-services/service-desktop/endpoints/Context.hpp
M module-services/service-desktop/endpoints/EndpointFactory.hpp
D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsEndpoint.cpp
D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsEndpoint.hpp
D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp
D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.hpp
M module-services/service-desktop/parser/ParserUtils.hpp
M module-services/service-time/CMakeLists.txt
M module-services/service-time/ServiceTime.cpp
M module-services/service-time/ServiceTime.hpp
D module-services/service-time/service-time/CalendarTimeEvents.hpp
D module-services/service-time/service-time/TimeEvents.hpp
M module-services/service-time/service-time/TimeMessage.hpp
M module-services/service-time/service-time/TimeSettings.hpp
D module-services/service-time/timeEvents/CalendarTimeEvents.cpp
D module-services/service-time/timeEvents/TimeEvents.cpp
M module-sys/Service/Common.hpp
M source/MessageType.hpp
D art/phone/home_notifications/calendar_notification_icon.png => art/phone/home_notifications/calendar_notification_icon.png +0 -0
D image/assets/images/calendar_notification_icon.vpi => image/assets/images/calendar_notification_icon.vpi +0 -0
M image/assets/lang/English.json => image/assets/lang/English.json +1 -37
@@ 113,44 113,9 @@
  "app_alarm_clock_snooze_30_min": "30 min",
  "app_alarm_clock_play_pause": "PLAY/PAUSE",
  "app_alarm_clock_edit": "EDIT",
  "app_alarm_clock_custom_repeat_title": "Custom repeat",
  "app_calendar_title_main": "Calendar",
  "app_calendar_options_edit": "Edit",
  "app_calendar_options_delete": "Delete",
  "app_calendar_event_delete_confirmation": "Delete this event?",
  "app_calendar_bar_month": "MONTH",
  "app_calendar_bar_list": "LIST",
  "app_calendar_event_detail": "Event",
  "app_calendar_event_detail_repeat": "Repeat",
  "app_calendar_event_detail_reminder": "Reminder",
  "app_calendar_no_events_information": "<text align='center' color='9'>No events yet.<p>Press <b>left arrow</b> to add new.</p></text>",
  "app_calendar_repeat_never": "Never",
  "app_calendar_repeat_daily": "Daily",
  "app_calendar_repeat_weekly": "Weekly",
  "app_calendar_repeat_two_weeks": "Two weeks",
  "app_calendar_repeat_month": "Month",
  "app_calendar_repeat_year": "Year",
  "app_calendar_repeat_custom": "Custom",
  "app_calendar_reminder_never": "Never",
  "app_calendar_reminder_event_time": "At the time of the event",
  "app_calendar_reminder_5_min_before": "5 mins before",
  "app_calendar_reminder_15_min_before": "15 mins before",
  "app_calendar_reminder_30_min_before": "30 mins before",
  "app_calendar_reminder_1_hour_before": "1h before",
  "app_calendar_reminder_2_hour_before": "2h before",
  "app_calendar_reminder_1_day_before": "1 day before",
  "app_calendar_reminder_2_days_before": "2 days before",
  "app_calendar_reminder_1_week_before": "1 week before",
  "app_calendar_custom_repeat_title": "Custom repeat",
  "app_calendar_new_event_title": "New event",
  "app_calendar_edit_event_title": "Edit event",
  "app_calendar_edit": "EDIT",
  "app_calendar_new_edit_event_name": "Event name",
  "app_calendar_new_edit_event_allday": "All day event",
  "app_calendar_all_day": "All day",
  "app_calendar_new_edit_event_start": "Start",
  "app_calendar_new_edit_event_end": "End",
  "app_calendar_event_error_dates": "The start date must be before\nthe end date",
  "app_calendar_event_error_empty_name": "Event name must not be empty",
  "app_calculator_title_main": "Calculator",
  "app_calculator_equals": "EQUALS",
  "app_calculator_decimal_separator": ".",


@@ 377,7 342,6 @@
  "app_settings_apps_phone": "Phone",
  "app_settings_apps_messages": "Messages",
  "app_settings_show_unread_first": "Show unread first",
  "app_settings_apps_calendar": "Calendar",
  "app_settings_apps_alarm_clock": "Alarm clock",
  "app_settings_vibration": "Vibration",
  "app_settings_sound": "Sound",

M image/assets/lang/Svenska.json => image/assets/lang/Svenska.json +1 -34
@@ 102,42 102,9 @@
  "app_alarm_clock_snooze_30_min": "30 min",
  "app_alarm_clock_play_pause": "SPELA/PAUSA",
  "app_alarm_clock_edit": "REDIGERA",
  "app_alarm_clock_custom_repeat_title": "Välj veckodagar",
  "app_calendar_title_main": "Kalender",
  "app_calendar_options_edit": "Redigera",
  "app_calendar_options_delete": "Radera",
  "app_calendar_event_delete_confirmation": "Radera den här händelsen?",
  "app_calendar_bar_month": "MÅNAD",
  "app_calendar_bar_list": "LISTA",
  "app_calendar_event_detail": "Händelse",
  "app_calendar_event_detail_repeat": "Upprepa",
  "app_calendar_event_detail_reminder": "Påminnelse",
  "app_calendar_no_events_information": "<text align='center' color='9'>Inga händelser än.<p>Tryck <b>vänsterpil</b> för att lägga till en.</p></text>",
  "app_calendar_repeat_never": "Ingen upprepning",
  "app_calendar_repeat_daily": "Varje dag",
  "app_calendar_repeat_weekly": "Varje vecka",
  "app_calendar_repeat_two_weeks": "Varannan vecka",
  "app_calendar_repeat_month": "Varje månad",
  "app_calendar_repeat_year": "Varje år",
  "app_calendar_repeat_custom": "Valfria veckodagar",
  "app_calendar_reminder_never": "Ingen påminnelse",
  "app_calendar_reminder_event_time": "När händelsen startar",
  "app_calendar_reminder_5_min_before": "5 minuter före",
  "app_calendar_reminder_15_min_before": "15 minuter före",
  "app_calendar_reminder_30_min_before": "30 minuter före",
  "app_calendar_reminder_1_hour_before": "1h före",
  "app_calendar_reminder_2_hour_before": "2h före",
  "app_calendar_reminder_1_day_before": "En dag före",
  "app_calendar_reminder_2_days_before": "Två dagar före",
  "app_calendar_reminder_1_week_before": "En vecka före",
  "app_calendar_custom_repeat_title": "Välj veckodagar",
  "app_calendar_new_event_title": "Ny händelse",
  "app_calendar_edit_event_title": "Redigera händelse",
  "app_calendar_edit": "REDIGERA",
  "app_calendar_new_edit_event_name": "Händelsens namn",
  "app_calendar_new_edit_event_allday": "Heldag",
  "app_calendar_all_day": "Hela dagen",
  "app_calendar_new_edit_event_start": "Början",
  "app_calendar_new_edit_event_end": "Slut",
  "app_calculator_title_main": "Miniräknare",
  "app_calculator_equals": "RESULTAT",
  "app_calculator_decimal_separator": ",",

M module-apps/application-alarm-clock/windows/CustomRepeatWindow.cpp => module-apps/application-alarm-clock/windows/CustomRepeatWindow.cpp +1 -1
@@ 23,7 23,7 @@ namespace app::alarmClock
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::save));

        setTitle(utils::translate("app_calendar_custom_repeat_title"));
        setTitle(utils::translate("app_alarm_clock_custom_repeat_title"));
        list = new gui::ListView(this,
                                 style::alarmClock::window::listView_x,
                                 style::alarmClock::window::listView_y,

M module-apps/application-calendar/ApplicationCalendar.cpp => module-apps/application-calendar/ApplicationCalendar.cpp +3 -141
@@ 2,48 2,10 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ApplicationCalendar.hpp"
#include "DialogMetadataMessage.hpp"
#include "NoEvents.hpp"
#include "Dialog.hpp"

#include <application-calendar/windows/CalendarMainWindow.hpp>
#include <application-calendar/windows/DayEventsWindow.hpp>
#include <application-calendar/windows/CalendarEventsOptionsWindow.hpp>
#include <application-calendar/windows/AllEventsWindow.hpp>
#include <application-calendar/windows/EventDetailWindow.hpp>
#include <application-calendar/windows/NewEditEventWindow.hpp>
#include <application-calendar/windows/CustomRepeatWindow.hpp>
#include <application-calendar/windows/EventReminderWindow.hpp>
#include <apps-common/messages/DialogMetadataMessage.hpp>
#include <module-db/queries/calendar/QueryEventsAdd.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/QueryMessage.hpp>
#include <service-db/DBNotificationMessage.hpp>

#include <ctime>

namespace app
{
    const std::map<Reminder, const char *> ApplicationCalendar::reminderOptions = {
        {Reminder::never, "app_calendar_reminder_never"},
        {Reminder::event_time, "app_calendar_reminder_event_time"},
        {Reminder::five_min_before, "app_calendar_reminder_5_min_before"},
        {Reminder::fifteen_min_before, "app_calendar_reminder_15_min_before"},
        {Reminder::thirty_min_before, "app_calendar_reminder_30_min_before"},
        {Reminder::one_hour_before, "app_calendar_reminder_1_hour_before"},
        {Reminder::two_hour_before, "app_calendar_reminder_2_hour_before"},
        {Reminder::one_day_before, "app_calendar_reminder_1_day_before"},
        {Reminder::two_days_before, "app_calendar_reminder_2_days_before"},
        {Reminder::one_week_before, "app_calendar_reminder_1_week_before"}};

    const std::map<Repeat, const char *> ApplicationCalendar::repeatOptions = {
        {Repeat::never, "app_calendar_repeat_never"},
        {Repeat::daily, "app_calendar_repeat_daily"},
        {Repeat::weekly, "app_calendar_repeat_weekly"},
        {Repeat::biweekly, "app_calendar_repeat_two_weeks"},
        {Repeat::monthly, "app_calendar_repeat_month"},
        {Repeat::yearly, "app_calendar_repeat_year"}};

    ApplicationCalendar::ApplicationCalendar(std::string name,
                                             std::string parent,
                                             sys::phone_modes::PhoneMode mode,


@@ 51,13 13,7 @@ namespace app
                                             uint32_t stackDepth,
                                             sys::ServicePriority priority)
        : Application(name, parent, mode, startInBackground, stackDepth, priority)
    {
        bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);
        addActionReceiver(manager::actions::ShowReminder, [this](auto &&data) {
            switchWindow(style::window::calendar::name::event_reminder_window, std::move(data));
            return actionHandled();
        });
    }
    {}

    sys::MessagePointer ApplicationCalendar::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
    {


@@ 66,39 22,11 @@ namespace app
        if (retMsg && (dynamic_cast<sys::ResponseMessage *>(retMsg.get())->retCode == sys::ReturnCodes::Success)) {
            return retMsg;
        }

        auto msg = dynamic_cast<db::NotificationMessage *>(msgl);
        if (msg != nullptr) {
            LOG_DEBUG("Received notification");
            // window-specific actions
            if (msg->interface == db::Interface::Name::Events) {
                for (auto &[name, window] : windowsStack.windows) {
                    window->onDatabaseMessage(msg);
                }
            }
            return std::make_shared<sys::ResponseMessage>();
        }

        // this variable defines whether message was processed.
        bool handled = false;
        // handle database response
        if (resp != nullptr) {
            handled = true;
            if (auto command = callbackStorage->getCallback(resp); command->execute()) {
                refreshWindow(gui::RefreshModes::GUI_REFRESH_FAST);
            }
        }
        if (handled) {
            return std::make_shared<sys::ResponseMessage>();
        }
        else {
            return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Unresolved);
        }
        return std::make_shared<sys::ResponseMessage>();
    }

    sys::ReturnCodes ApplicationCalendar::InitHandler()
    {
        applicationStartTime = std::time(nullptr);
        auto ret             = Application::InitHandler();
        createUserInterface();
        return ret;


@@ 106,47 34,14 @@ namespace app

    sys::ReturnCodes ApplicationCalendar::DeinitHandler()
    {
        return sys::ReturnCodes::Success;
        return Application::DeinitHandler();
    }

    void ApplicationCalendar::createUserInterface()
    {
        using namespace style::window::calendar::name;

        windowsFactory.attach(gui::name::window::main_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::CalendarMainWindow>(app, name);
        });
        windowsFactory.attach(day_events_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DayEventsWindow>(app);
        });
        windowsFactory.attach(no_events_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::NoEvents>(app, name);
        });
        windowsFactory.attach(style::window::calendar::name::events_options,
                              [](Application *app, const std::string &name) {
                                  return std::make_unique<gui::CalendarEventsOptions>(app);
                              });
        windowsFactory.attach(dialog_yes_no, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DialogYesNo>(app, dialog_yes_no);
        });
        windowsFactory.attach(all_events_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::AllEventsWindow>(app, all_events_window);
        });
        windowsFactory.attach(details_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::EventDetailWindow>(app, details_window);
        });
        windowsFactory.attach(new_edit_event, [](Application *app, const std::string &name) {
            return std::make_unique<gui::NewEditEventWindow>(app, new_edit_event);
        });
        windowsFactory.attach(custom_repeat_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::CustomRepeatWindow>(app, custom_repeat_window);
        });
        windowsFactory.attach(event_reminder_window, [](Application *app, const std::string &name) {
            return std::make_unique<gui::EventReminderWindow>(app, event_reminder_window);
        });
        windowsFactory.attach(gui::window::name::dialog_confirm, [](Application *app, const std::string &name) {
            return std::make_unique<gui::DialogConfirm>(app, name);
        });

        attachPopups(
            {gui::popup::ID::Volume, gui::popup::ID::Tethering, gui::popup::ID::PhoneModes, gui::popup::ID::PhoneLock});


@@ 155,37 50,4 @@ namespace app
    void ApplicationCalendar::destroyUserInterface()
    {}

    void ApplicationCalendar::switchToNoEventsWindow(const std::string &title, const TimePoint &dateFilter)
    {
        if (equivalentWindow == EquivalentWindow::DayEventsWindow) {
            popToWindow(gui::name::window::main_window);
        }
        if (equivalentWindow == EquivalentWindow::AllEventsWindow) {
            popToWindow(gui::name::window::main_window);
        }

        LOG_DEBUG("Switch to no events window");

        auto metaData = std::make_unique<gui::DialogMetadataMessage>(gui::DialogMetadata{
            title,
            "phonebook_empty_grey_circle_W_G",
            utils::translate("app_calendar_no_events_information"),
            "",
            [=]() -> bool {
                LOG_DEBUG("Switch to new event window");
                auto event       = std::make_shared<EventsRecord>();
                event->date_from = dateFilter;
                event->date_till = dateFilter + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                                   std::chrono::minutes(utils::time::Locale::max_minutes);

                auto eventData = std::make_unique<EventRecordData>(std::move(event));
                eventData->setDescription(style::window::calendar::new_event);
                switchWindow(
                    style::window::calendar::name::new_edit_event, gui::ShowMode::GUI_SHOW_INIT, std::move(eventData));
                return true;
            }});

        switchWindow(style::window::calendar::name::no_events_window, std::move(metaData));
    }

} /* namespace app */

M module-apps/application-calendar/ApplicationCalendar.hpp => module-apps/application-calendar/ApplicationCalendar.hpp +1 -29
@@ 6,10 6,6 @@
#include "Application.hpp"
#include "Service/Message.hpp"
#include "SystemManager/SystemManager.hpp"
#include "gui/widgets/Label.hpp"
#include <module-apps/application-calendar/data/dateCommon.hpp>
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "NoEvents.hpp"

namespace app
{


@@ 18,10 14,6 @@ namespace app

    class ApplicationCalendar : public Application
    {
        time_t applicationStartTime       = 0;
        int eventShift                    = 0;
        EquivalentWindow equivalentWindow = EquivalentWindow::EmptyWindow;

      public:
        ApplicationCalendar(std::string name,
                            std::string parent,


@@ 38,35 30,15 @@ namespace app
        {
            return sys::ReturnCodes::Success;
        }
        time_t getCurrentTimeStamp()
        {
            return applicationStartTime;
        }
        void setEquivalentToEmptyWindow(EquivalentWindow window)
        {
            equivalentWindow = window;
        };
        EquivalentWindow getEquivalentToEmptyWindow() const
        {
            return equivalentWindow;
        };
        int getEventShift()
        {
            return eventShift;
        }
        void createUserInterface() override;
        void destroyUserInterface() override;
        void switchToNoEventsWindow(const std::string &title = "", const TimePoint &dateFilter = TimePoint());

        static const std::map<Reminder, const char *> reminderOptions;
        static const std::map<Repeat, const char *> repeatOptions;
    };

    template <> struct ManifestTraits<ApplicationCalendar>
    {
        static auto GetManifest() -> manager::ApplicationManifest
        {
            return {{manager::actions::Launch, manager::actions::ShowReminder, manager::actions::PhoneModeChanged}};
            return {{manager::actions::Launch, manager::actions::PhoneModeChanged}};
        }
    };
} /* namespace app */

M module-apps/application-calendar/CMakeLists.txt => module-apps/application-calendar/CMakeLists.txt +0 -26
@@ 5,37 5,11 @@
		"${CMAKE_CURRENT_LIST_DIR}/data/CalendarData.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/MonthModel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/CalendarMainWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/DayEventsWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/CalendarEventsOptionsWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/AllEventsWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/EventDetailWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/NewEditEventWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/CustomRepeatWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/AllEventsModel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/EventDetailModel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/EventDetailWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/windows/EventReminderWindow.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/CustomRepeatModel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/NewEditEventModel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/DayEventsModel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/DayEventsItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/AllEventsItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CalendarListView.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/EventDetailDescriptionItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/RepeatAndReminderItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CheckBoxWithLabelItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/SeveralOptionsItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/TextWithLabelItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/NewEventCheckBoxWithLabel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/DayLabel.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/MonthBox.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CalendarDateItem.cpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CalendarTimeItem.cpp"
	PUBLIC
		"${CMAKE_CURRENT_LIST_DIR}/ApplicationCalendar.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/models/MonthModel.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CalendarListItem.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/widgets/CalendarStyle.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/data/OptionParser.hpp"
		"${CMAKE_CURRENT_LIST_DIR}/data/CalendarData.hpp"
)

M module-apps/application-calendar/data/CalendarData.hpp => module-apps/application-calendar/data/CalendarData.hpp +2 -14
@@ 2,7 2,8 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "module-db/Interface/EventsRecord.hpp"

#include "dateCommon.hpp"
#include <module-gui/gui/SwitchData.hpp>
#include "application-calendar/widgets/CalendarStyle.hpp"



@@ 12,19 13,6 @@ enum class EventAction
    Edit
};

class EventRecordData : public gui::SwitchData
{
  protected:
    std::shared_ptr<EventsRecord> record;

  public:
    explicit EventRecordData(std::shared_ptr<EventsRecord> record) : record{std::move(record)} {};
    std::shared_ptr<EventsRecord> getData()
    {
        return record;
    };
};

class WeekDaysRepeatData : public gui::SwitchData
{
  protected:

D module-apps/application-calendar/models/AllEventsModel.cpp => module-apps/application-calendar/models/AllEventsModel.cpp +0 -91
@@ 1,91 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AllEventsModel.hpp"
#include "application-calendar/widgets/AllEventsItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include "module-apps/application-calendar/ApplicationCalendar.hpp"
#include <ListView.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <queries/calendar/QueryEventsGetAllLimited.hpp>

AllEventsModel::AllEventsModel(app::Application *app) : DatabaseModel(app), app::AsyncCallbackReceiver{app}
{
    application = app;
    assert(app != nullptr);
}

unsigned int AllEventsModel::requestRecordsCount()
{
    return recordsCount;
}

void AllEventsModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    auto query = std::make_unique<db::query::events::GetAllLimited>(offset, limit);
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);
}

unsigned int AllEventsModel::getMinimalItemHeight() const
{
    return style::window::label::big_h;
}

gui::ListItem *AllEventsModel::getItem(gui::Order order)
{
    auto record = getRecord(order);
    if (record == nullptr) {
        LOG_DEBUG("Empty record in AllEventsModel::GetItem");
        return nullptr;
    }

    auto *item = new gui::AllEventsItem();
    item->setEvent(record);
    item->activatedCallback = [=](gui::Item &item) {
        LOG_INFO("Switch to event details window");
        auto rec  = std::make_unique<EventsRecord>(*record);
        auto data = std::make_unique<EventRecordData>(std::move(rec));
        application->switchWindow(style::window::calendar::name::details_window, std::move(data));
        return true;
    };

    return item;
}

bool AllEventsModel::updateRecords(std::vector<EventsRecord> records)
{
    DatabaseModel::updateRecords(std::move(records));
    list->onProviderDataUpdate();
    return true;
}

auto AllEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
{
    auto response = dynamic_cast<db::query::events::GetAllLimitedResult *>(queryResult);
    assert(response != nullptr);

    auto records = response->getResult();
    list->setElementsCount(response->getCountResult());

    auto app = dynamic_cast<app::ApplicationCalendar *>(application);
    assert(application != nullptr);

    if (records.empty()) {

        if (app->getEquivalentToEmptyWindow() == EquivalentWindow::AllEventsWindow) {
            auto filter = TimePointNow();
            app->switchToNoEventsWindow(utils::translate("app_calendar_title_main"), filter);
        }
    }
    auto eventShift = app->getEventShift();
    if (eventShift) {
        for (auto &record : records) {
            record.date_from += std::chrono::hours(eventShift);
            record.date_till += std::chrono::hours(eventShift);
        }
    }
    return this->updateRecords(std::move(records));
}

D module-apps/application-calendar/models/AllEventsModel.hpp => module-apps/application-calendar/models/AllEventsModel.hpp +0 -28
@@ 1,28 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "Application.hpp"
#include <ListItemProvider.hpp>
#include <DatabaseModel.hpp>
#include <module-db/Interface/EventsRecord.hpp>
#include <module-db/Common/Query.hpp>

class AllEventsModel : public app::DatabaseModel<EventsRecord>,
                       public gui::ListItemProvider,
                       public app::AsyncCallbackReceiver
{
    app::Application *application  = nullptr;

  public:
    AllEventsModel(app::Application *app);

    void requestRecords(const uint32_t offset, const uint32_t limit) override;
    bool updateRecords(std::vector<EventsRecord> records) override;

    auto handleQueryResponse(db::QueryResult *) -> bool;
    // virtual methods for ListViewProvider
    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    gui::ListItem *getItem(gui::Order order) override;
    [[nodiscard]] unsigned int requestRecordsCount() override;
};

D module-apps/application-calendar/models/CustomRepeatModel.cpp => module-apps/application-calendar/models/CustomRepeatModel.cpp +0 -78
@@ 1,78 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CustomRepeatModel.hpp"
#include "application-calendar/widgets/CheckBoxWithLabelItem.hpp"
#include <ListView.hpp>
#include <Utils.hpp>

CustomRepeatModel::CustomRepeatModel(app::Application *app) : application(app)
{}

unsigned int CustomRepeatModel::requestRecordsCount()
{
    return internalData.size();
}

unsigned int CustomRepeatModel::getMinimalItemHeight() const
{
    return style::window::calendar::item::checkBox::height;
}

void CustomRepeatModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    setupModel(offset, limit);
    list->onProviderDataUpdate();
}

gui::ListItem *CustomRepeatModel::getItem(gui::Order order)
{
    return getRecord(order);
}

void CustomRepeatModel::createData(const std::shared_ptr<WeekDaysRepeatData> &data)
{
    internalData.push_back(
        new gui::CheckBoxWithLabelItem(application, utils::translate(style::strings::common::Monday), data));
    internalData.push_back(
        new gui::CheckBoxWithLabelItem(application, utils::translate(style::strings::common::Tuesday), data));
    internalData.push_back(
        new gui::CheckBoxWithLabelItem(application, utils::translate(style::strings::common::Wednesday), data));
    internalData.push_back(
        new gui::CheckBoxWithLabelItem(application, utils::translate(style::strings::common::Thursday), data));
    internalData.push_back(
        new gui::CheckBoxWithLabelItem(application, utils::translate(style::strings::common::Friday), data));
    internalData.push_back(
        new gui::CheckBoxWithLabelItem(application, utils::translate(style::strings::common::Saturday), data));
    internalData.push_back(
        new gui::CheckBoxWithLabelItem(application, utils::translate(style::strings::common::Sunday), data));

    for (auto &item : internalData) {
        item->deleteByList = false;
    }
}

void CustomRepeatModel::loadData(const std::shared_ptr<WeekDaysRepeatData> &data)
{
    list->reset();
    eraseInternalData();

    createData(data);

    list->rebuildList();
}

std::vector<bool> CustomRepeatModel::getIsCheckedData()
{
    std::vector<bool> isCheckedData;
    for (auto item : internalData) {
        if (item->onContentChangedCallback && item->onContentChangedCallback()) {
            isCheckedData.push_back(true);
        }
        else {
            isCheckedData.push_back(false);
        }
    }

    return isCheckedData;
}

D module-apps/application-calendar/models/CustomRepeatModel.hpp => module-apps/application-calendar/models/CustomRepeatModel.hpp +0 -30
@@ 1,30 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Application.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/widgets/CalendarListItem.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>

class CustomRepeatModel : public app::InternalModel<gui::CalendarListItem *>, public gui::ListItemProvider
{
    app::Application *application = nullptr;

  public:
    CustomRepeatModel(app::Application *app);

    void loadData(const std::shared_ptr<WeekDaysRepeatData> &data);

    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    [[nodiscard]] unsigned int requestRecordsCount() override;
    gui::ListItem *getItem(gui::Order order) override;
    void requestRecords(const uint32_t offset, const uint32_t limit) override;
    std::vector<bool> getIsCheckedData();

  private:
    void createData(const std::shared_ptr<WeekDaysRepeatData> &data);
};

D module-apps/application-calendar/models/DayEventsModel.cpp => module-apps/application-calendar/models/DayEventsModel.cpp +0 -104
@@ 1,104 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DayEventsModel.hpp"
#include "application-calendar/widgets/DayEventsItem.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include <ListView.hpp>
#include <queries/calendar/QueryEventsGetFilteredByDay.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <service-db/QueryMessage.hpp>
#include <module-db/queries/RecordQuery.hpp>

DayEventsModel::DayEventsModel(app::Application *app)
    : DatabaseModel(app), app::AsyncCallbackReceiver{app}, application(app)
{}

unsigned int DayEventsModel::requestRecordsCount()
{
    return recordsCount;
}

unsigned int DayEventsModel::getMinimalItemHeight() const
{
    return style::window::calendar::item::dayEvents::height;
}

void DayEventsModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    auto query = std::make_unique<db::query::events::GetFilteredByDay>(filterFrom, offset, limit);
    auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
    task->setCallback([this](auto response) { return handleQueryResponse(response); });
    task->execute(application, this);
}

gui::ListItem *DayEventsModel::getItem(gui::Order order)
{
    auto record = getRecord(order);
    if (record == nullptr) {
        LOG_DEBUG("Empty record in DayEventsModel::GetItem");
        return nullptr;
    }

    auto *item = new gui::DayEventsItem();
    item->setEvent(record);
    item->activatedCallback = [=](gui::Item &item) {
        LOG_INFO("Switch to event details window");
        auto rec  = std::make_unique<EventsRecord>(*record);
        auto data = std::make_unique<EventRecordData>(std::move(rec));
        application->switchWindow(style::window::calendar::name::details_window, std::move(data));
        return true;
    };

    return item;
}

bool DayEventsModel::updateRecords(std::vector<EventsRecord> records)
{
    DatabaseModel::updateRecords(std::move(records));
    list->onProviderDataUpdate();
    return true;
}

auto DayEventsModel::handleQueryResponse(db::QueryResult *queryResult) -> bool
{
    if (auto response = dynamic_cast<db::query::events::GetFilteredByDayResult *>(queryResult); response != nullptr) {
        if (recordsCount != (response->getCountResult())) {
            recordsCount = response->getCountResult();
            list->rebuildList(gui::listview::RebuildType::Full, 0, true);
            return false;
        }

        if (auto app = dynamic_cast<app::ApplicationCalendar *>(application); app != nullptr) {
            if (response->getCountResult() == 0) {
                LOG_DEBUG("Empty records");
                if (app->getEquivalentToEmptyWindow() == EquivalentWindow::DayEventsWindow) {
                    app->switchToNoEventsWindow(dayMonthTitle, filterFrom);
                    return true;
                }
            }
            auto eventShift = app->getEventShift();
            auto records    = response->getResult();
            if (eventShift) {
                for (auto &record : records) {
                    record.date_from += std::chrono::hours(eventShift);
                    record.date_till += std::chrono::hours(eventShift);
                }
            }
            return updateRecords(std::move(records));
        }
        LOG_ERROR("App is not a calendar");
        return false;
    }
    LOG_ERROR("GetFilteredResult response is nullptr");
    return false;
}

void DayEventsModel::setFilters(TimePoint from, TimePoint till, const std::string &dayMonth)
{
    /// TODO: change to one filter
    this->filterFrom    = from;
    this->filterTill    = till;
    this->dayMonthTitle = dayMonth;
}

D module-apps/application-calendar/models/DayEventsModel.hpp => module-apps/application-calendar/models/DayEventsModel.hpp +0 -33
@@ 1,33 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "Application.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "DatabaseModel.hpp"
#include <ListItem.hpp>
#include <ListItemProvider.hpp>
#include <module-db/Interface/EventsRecord.hpp>

class DayEventsModel : public app::DatabaseModel<EventsRecord>,
                       public gui::ListItemProvider,
                       public app::AsyncCallbackReceiver
{
    app::Application *application = nullptr;
    std::string dayMonthTitle;
    TimePoint filterFrom = TIME_POINT_INVALID;
    TimePoint filterTill = TIME_POINT_INVALID;

  public:
    DayEventsModel(app::Application *app);

    void setFilters(TimePoint from, TimePoint till, const std::string &dayMonth);

    bool updateRecords(std::vector<EventsRecord> records) override;
    auto handleQueryResponse(db::QueryResult *) -> bool;

    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    [[nodiscard]] unsigned int requestRecordsCount() override;
    gui::ListItem *getItem(gui::Order order) override;
    void requestRecords(const uint32_t offset, const uint32_t limit) override;
};

D module-apps/application-calendar/models/EventDetailModel.cpp => module-apps/application-calendar/models/EventDetailModel.cpp +0 -60
@@ 1,60 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventDetailModel.hpp"
#include "application-calendar/widgets/EventDetailDescriptionItem.hpp"
#include "application-calendar/widgets/RepeatAndReminderItem.hpp"
#include <ListView.hpp>

EventDetailModel::EventDetailModel(app::Application *app) : application(app)
{}

auto EventDetailModel::requestRecordsCount() -> unsigned int
{
    return internalData.size();
}

unsigned int EventDetailModel::getMinimalItemHeight() const
{
    return style::window::calendar::item::eventDetail::height_max;
}

void EventDetailModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    setupModel(offset, limit);
    list->onProviderDataUpdate();
}

gui::ListItem *EventDetailModel::getItem(gui::Order order)
{
    return getRecord(order);
}

void EventDetailModel::createData()
{
    auto app = dynamic_cast<app::ApplicationCalendar *>(application);
    assert(app != nullptr);

    internalData.push_back(new gui::EventDetailDescriptionItem());
    internalData.push_back(new gui::RepeatAndReminderItem(app));

    for (auto &item : internalData) {
        item->deleteByList = false;
    }
}

void EventDetailModel::loadData(std::shared_ptr<EventsRecord> record)
{
    list->reset();
    eraseInternalData();

    createData();

    for (auto &item : internalData) {
        if (item->onLoadCallback) {
            item->onLoadCallback(record);
        }
    }

    list->rebuildList();
}

D module-apps/application-calendar/models/EventDetailModel.hpp => module-apps/application-calendar/models/EventDetailModel.hpp +0 -29
@@ 1,29 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/widgets/CalendarListItem.hpp"
#include "module-apps/application-calendar/ApplicationCalendar.hpp"
#include "Application.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>

class EventDetailModel : public app::InternalModel<gui::CalendarListItem *>, public gui::ListItemProvider
{
    app::Application *application = nullptr;

  public:
    EventDetailModel(app::Application *app);

    void loadData(std::shared_ptr<EventsRecord> record);

    [[nodiscard]] auto requestRecordsCount() -> unsigned int override;
    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    gui::ListItem *getItem(gui::Order order) override;
    void requestRecords(const uint32_t offset, const uint32_t limit) override;

  private:
    void createData();
};

M module-apps/application-calendar/models/MonthModel.cpp => module-apps/application-calendar/models/MonthModel.cpp +1 -53
@@ 1,33 1,10 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "MonthModel.hpp"
#include <time/time_locale.hpp>
#include <time/time_constants.hpp>

date::year_month_day MonthModel::getYearMonthDayFromTimePoint(TimePoint timePoint) const
{
    return date::year_month_day{date::floor<date::days>(timePoint)};
}

uint32_t MonthModel::getEventDurationInDays(const EventsRecord &record) const
{
    auto eventStartDuration = utils::time::Duration(TimePointToTimeT(record.date_from));
    auto eventEndDuration   = utils::time::Duration(TimePointToTimeT(record.date_till));
    auto startEventDurationSinceEpochInDaysRoundedDown =
        (eventStartDuration.getHours() - eventStartDuration.getHours() % utils::time::hoursInday) /
        utils::time::hoursInday;
    auto endEventDurationSinceEpochInDaysRoundedDown =
        (eventEndDuration.getHours() - eventEndDuration.getHours() % utils::time::hoursInday) / utils::time::hoursInday;
    return endEventDurationSinceEpochInDaysRoundedDown - startEventDurationSinceEpochInDaysRoundedDown;
}

uint32_t MonthModel::getDayIndex(TimePoint date) const
{
    date::year_month_day recordDate = TimePointToYearMonthDay(date);
    return (static_cast<unsigned>(recordDate.day()) - 1);
}

MonthModel::MonthModel(date::year_month_day yearMonthDay)
{
    this->yearMonth                            = YearMonth(yearMonthDay);


@@ 39,35 16,6 @@ MonthModel::MonthModel(date::year_month_day yearMonthDay)
    this->firstWeekDayNumb                     = date::weekday{yearMonthDayFirst}.c_encoding();
}

void MonthModel::markEventsInDays(const std::vector<EventsRecord> &records, std::array<bool, 31> &isDayEmpty)
{
    for (auto &rec : records) {
        auto eventYearMonthFrom = YearMonth(getYearMonthDayFromTimePoint(rec.date_from));
        auto eventYearMonthTill = YearMonth(getYearMonthDayFromTimePoint(rec.date_till));

        if (eventYearMonthFrom < this->yearMonth && this->yearMonth < eventYearMonthTill) {
            for (uint32_t i = 0; i < max_month_day; i++) {
                isDayEmpty[i] = false;
            }
            return;
        }

        int durationInDays = getEventDurationInDays(rec);

        if (this->yearMonth == eventYearMonthFrom) {
            for (int i = getDayIndex(rec.date_from); i < max_month_day && durationInDays >= 0; i++, durationInDays--) {
                isDayEmpty[i] = false;
            }
        }

        if (this->yearMonth == eventYearMonthTill) {
            for (int i = getDayIndex(rec.date_till); i >= 0 && durationInDays >= 0; i--, durationInDays--) {
                isDayEmpty[i] = false;
            }
        }
    }
}

date::year MonthModel::getYear()
{
    return year;

M module-apps/application-calendar/models/MonthModel.hpp => module-apps/application-calendar/models/MonthModel.hpp +1 -15
@@ 3,17 3,9 @@

#pragma once

#include "windows/AppWindow.hpp"
#include "Application.hpp"
#include <gui/widgets/GridLayout.hpp>
#include <gui/widgets/Item.hpp>
#include <Text.hpp>
#include <date/date.h>
#include <map>
#include <module-apps/application-calendar/data/dateCommon.hpp>
#include <string>
#include <vector>
#include <module-apps/application-calendar/data/dateCommon.hpp>
#include <module-db/Interface/EventsRecord.hpp>

class YearMonth
{


@@ 55,17 47,11 @@ class MonthModel
  private:
    YearMonth yearMonth;

    [[nodiscard]] date::year_month_day getYearMonthDayFromTimePoint(TimePoint timePoint) const;
    [[nodiscard]] uint32_t getEventDurationInDays(const EventsRecord &records) const;
    [[nodiscard]] uint32_t getDayIndex(TimePoint date) const;

  public:
    explicit MonthModel(date::year_month_day yearMonthDay);
    MonthModel()          = default;
    virtual ~MonthModel() = default;

    void markEventsInDays(const std::vector<EventsRecord> &records, std::array<bool, 31> &isDayEmpty);

    date::year getYear();
    date::month getMonth();
    uint32_t getLastDay();

D module-apps/application-calendar/models/NewEditEventModel.cpp => module-apps/application-calendar/models/NewEditEventModel.cpp +0 -268
@@ 1,268 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NewEditEventModel.hpp"
#include "AppWindow.hpp"
#include <application-calendar/widgets/NewEventCheckBoxWithLabel.hpp>
#include <application-calendar/widgets/TextWithLabelItem.hpp>
#include <application-calendar/widgets/CalendarStyle.hpp>
#include <application-calendar/widgets/CalendarDateItem.hpp>
#include <application-calendar/widgets/CalendarTimeItem.hpp>
#include <application-calendar/widgets/SeveralOptionsItem.hpp>
#include <application-calendar/data/CalendarData.hpp>
#include <application-calendar/ApplicationCalendar.hpp>
#include <module-db/queries/calendar/QueryEventsAdd.hpp>
#include <module-db/queries/calendar/QueryEventsEdit.hpp>
#include <apps-common/messages/DialogMetadataMessage.hpp>

#include <service-db/DBServiceAPI.hpp>
#include <time/time_conversion.hpp>
#include <ListView.hpp>

NewEditEventModel::NewEditEventModel(app::Application *app) : application(app)
{}

unsigned int NewEditEventModel::requestRecordsCount()
{
    return internalData.size();
}

unsigned int NewEditEventModel::getMinimalItemHeight() const
{
    return style::window::calendar::item::checkBox::height;
}

void NewEditEventModel::requestRecords(const uint32_t offset, const uint32_t limit)
{
    setupModel(offset, limit);
    list->onProviderDataUpdate();
}

gui::ListItem *NewEditEventModel::getItem(gui::Order order)
{
    return getRecord(order);
}

void NewEditEventModel::createData(bool allDayEvent)
{
    auto app = application;
    assert(app != nullptr);

    eventNameInput = new gui::TextWithLabelItem(
        utils::translate("app_calendar_new_edit_event_name"),
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
        [app]() { app->getCurrentWindow()->selectSpecialCharacter(); });

    allDayEventCheckBox = new gui::NewEventCheckBoxWithLabel(
        application, utils::translate("app_calendar_new_edit_event_allday"), [this](bool isChecked) {
            isChecked ? createTimeItems() : eraseTimeItems();
            const auto it = std::find(std::begin(internalData), std::end(internalData), allDayEventCheckBox);
            list->rebuildList(gui::listview::RebuildType::OnPageElement, std::distance(std::begin(internalData), it));
        });

    dateItem = new gui::CalendarDateItem();

    reminder = new gui::SeveralOptionsItem(
        application,
        utils::translate("app_calendar_event_detail_reminder"),
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

    repeat = new gui::SeveralOptionsItem(
        application,
        utils::translate("app_calendar_event_detail_repeat"),
        [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
        [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

    allDayEventCheckBox->setConnectionToDateItem(dateItem->getDateWidget());

    internalData.push_back(eventNameInput);
    internalData.push_back(allDayEventCheckBox);
    internalData.push_back(dateItem);
    internalData.push_back(reminder);
    internalData.push_back(repeat);

    if (!allDayEvent) {
        createTimeItems();
    }
    else {
        startTimeBuffer +=
            TimePointToHourMinSec(TimePointNow()).hours() + TimePointToHourMinSec(TimePointNow()).minutes();
        endTimeBuffer = startTimeBuffer + std::chrono::hours(1);
    }

    for (auto &item : internalData) {
        item->deleteByList = false;
    }
}

void NewEditEventModel::loadData(std::shared_ptr<EventsRecord> record)
{
    clearData();
    startTimeBuffer = record->date_from;
    endTimeBuffer   = record->date_till;

    createData(gui::allDayEvents::isAllDayEvent(startTimeBuffer, endTimeBuffer));

    for (auto &item : internalData) {
        if (item->onLoadCallback) {
            item->onLoadCallback(record);
        }
    }
    list->rebuildList();
}

void NewEditEventModel::loadRepeat(const std::shared_ptr<EventsRecord> &record)
{
    if (repeat->onLoadCallback) {
        repeat->onLoadCallback(record);
    }
}

void NewEditEventModel::saveData(std::shared_ptr<EventsRecord> event, EventAction action)
{
    for (auto &item : internalData) {
        if (item->onSaveCallback) {
            item->onSaveCallback(event);
        }
    }

    std::string errorMessage;
    if (!isDataCorrect(event, errorMessage)) {

        std::string windowTitle = (action == EventAction::Edit) ? utils::translate("app_calendar_edit_event_title")
                                                                : utils::translate("app_calendar_new_event_title");

        application->switchWindow(
            gui::window::name::dialog_confirm,
            gui::ShowMode::GUI_SHOW_INIT,
            std::make_unique<gui::DialogMetadataMessage>(gui::DialogMetadata{
                std::move(windowTitle), "emergency_W_G", std::move(errorMessage), "", [=]() -> bool {
                    application->returnToPreviousWindow();
                    return true;
                }}));

        return;
    }

    if (action == EventAction::Edit) {
        saveEditData(event);
    }
    else {
        saveNewData(event);
    }

    clearData();
}

void NewEditEventModel::saveNewData(std::shared_ptr<EventsRecord> event)
{
    DBServiceAPI::GetQuery(application, db::Interface::Name::Events, std::make_unique<db::query::events::Add>(*event));

    auto data            = std::make_unique<DayMonthData>();
    auto startDate       = TimePointToYearMonthDay(event->date_from);
    auto filterDate      = TimePointFromYearMonthDay(startDate);
    std::string monthStr = utils::time::Locale::get_month(utils::time::Locale::Month(unsigned(startDate.month()) - 1));
    data->setData(std::to_string(unsigned(startDate.day())) + " " + monthStr, filterDate);

    if (application->getPrevWindow() == style::window::calendar::name::no_events_window) {
        auto app = dynamic_cast<app::ApplicationCalendar *>(application);
        assert(app != nullptr);
        if (app->getEquivalentToEmptyWindow() == EquivalentWindow::DayEventsWindow) {
            app->popToWindow(gui::name::window::main_window);
            app->switchWindow(style::window::calendar::name::day_events_window, std::move(data));
        }
        else if (app->getEquivalentToEmptyWindow() == EquivalentWindow::AllEventsWindow) {
            app->popToWindow(gui::name::window::main_window);
            app->switchWindow(style::window::calendar::name::all_events_window, std::move(data));
        }
    }
    else {
        if (application->getPrevWindow() == style::window::calendar::name::day_events_window) {
            application->switchWindow(style::window::calendar::name::day_events_window, std::move(data));
        }
        application->returnToPreviousWindow();
    }
}

void NewEditEventModel::saveEditData(std::shared_ptr<EventsRecord> event)
{
    event->reminder_fired = TIME_POINT_INVALID;
    if (!event->title.empty()) {
        DBServiceAPI::GetQuery(
            application, db::Interface::Name::Events, std::make_unique<db::query::events::Edit>(*event));
    }
    auto rec  = std::make_unique<EventsRecord>(*event);
    auto data = std::make_unique<EventRecordData>(std::move(rec));
    application->switchWindow(style::window::calendar::name::details_window, std::move(data));
}

bool NewEditEventModel::isDataCorrect(std::shared_ptr<EventsRecord> event, std::string &message)
{
    if (event->title.empty()) {
        LOG_WARN("Event data has empty title!");
        message = utils::translate("app_calendar_event_error_empty_name");
        return false;
    }

    if (event->date_from > event->date_till) {
        LOG_WARN("Event data has wrong dates!");
        message = utils::translate("app_calendar_event_error_dates");
        return false;
    }

    return true;
}

void NewEditEventModel::createTimeItems()
{
    auto create = [this](gui::CalendarTimeItem *&item, const std::string &description, gui::TimeWidget::Type type) {
        if (item == nullptr) {
            auto app = application;
            item     = new gui::CalendarTimeItem(
                description,
                type,
                [app](const UTF8 &text) { app->getCurrentWindow()->bottomBarTemporaryMode(text, false); },
                [app]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); },
                startTimeBuffer,
                endTimeBuffer);

            item->deleteByList = false;
            item->setConnectionToDateItem(dateItem);
            internalData.insert(std::find(internalData.begin(), internalData.end(), reminder), item);
        }
    };

    create(startTime, utils::translate("app_calendar_new_edit_event_start"), gui::TimeWidget::Type::Start);
    create(endTime, utils::translate("app_calendar_new_edit_event_end"), gui::TimeWidget::Type::End);

    startTime->setConnectionToSecondItem(endTime);
    endTime->setConnectionToSecondItem(startTime);
}

void NewEditEventModel::eraseTimeItems()
{
    auto erase = [this](gui::CalendarTimeItem *&item) {
        if (item != nullptr) {
            item->deleteByList = true;
            internalData.erase(std::find(internalData.begin(), internalData.end(), item));
            item = nullptr;
        }
    };

    startTimeBuffer = startTime->getFromTillDate()->from;
    endTimeBuffer   = endTime->getFromTillDate()->till;
    erase(startTime);
    erase(endTime);
}

void NewEditEventModel::clearData()
{
    list->reset();
    eraseInternalData();
    startTime       = nullptr;
    endTime         = nullptr;
    startTimeBuffer = TimePoint();
    endTimeBuffer   = TimePoint();
}

D module-apps/application-calendar/models/NewEditEventModel.hpp => module-apps/application-calendar/models/NewEditEventModel.hpp +0 -61
@@ 1,61 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "Application.hpp"
#include "InternalModel.hpp"
#include <ListItemProvider.hpp>
#include <application-calendar/widgets/CalendarListItem.hpp>
#include <application-calendar/data/CalendarData.hpp>

namespace gui
{
    class TextWithLabelItem;
    class NewEventCheckBoxWithLabel;
    class CalendarDateItem;
    class CalendarTimeItem;
    class SeveralOptionsItem;
} // namespace gui

class NewEditEventModel : public app::InternalModel<gui::CalendarListItem *>, public gui::ListItemProvider
{
    app::Application *application = nullptr;

    TimePoint startTimeBuffer;
    TimePoint endTimeBuffer;

    gui::TextWithLabelItem *eventNameInput              = nullptr;
    gui::NewEventCheckBoxWithLabel *allDayEventCheckBox = nullptr;
    gui::CalendarDateItem *dateItem  = nullptr;
    gui::CalendarTimeItem *startTime = nullptr;
    gui::CalendarTimeItem *endTime   = nullptr;
    gui::SeveralOptionsItem *reminder                   = nullptr;
    gui::SeveralOptionsItem *repeat                     = nullptr;

    void createData(bool allDayEvent);
    void createTimeItems();
    void eraseTimeItems();
    void clearData();

  public:
    explicit NewEditEventModel(app::Application *app);

    void loadData(std::shared_ptr<EventsRecord> record);
    void loadRepeat(const std::shared_ptr<EventsRecord> &record);
    void saveData(std::shared_ptr<EventsRecord> event, EventAction action);

    /// Check if the event data is correct.
    /// @param event event to be checked
    /// @param message string to write the error message in case event data is incorrect
    /// @return true if event data is correct or false elseway
    bool isDataCorrect(std::shared_ptr<EventsRecord> event, std::string &message);

    [[nodiscard]] unsigned int getMinimalItemHeight() const override;
    [[nodiscard]] unsigned int requestRecordsCount() override;
    gui::ListItem *getItem(gui::Order order) override;
    void requestRecords(const uint32_t offset, const uint32_t limit) override;

  protected:
    void saveNewData(std::shared_ptr<EventsRecord> event);
    void saveEditData(std::shared_ptr<EventsRecord> event);
};

D module-apps/application-calendar/widgets/AllEventsItem.cpp => module-apps/application-calendar/widgets/AllEventsItem.cpp +0 -86
@@ 1,86 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AllEventsItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "CalendarListItem.hpp"
#include <Style.hpp>
#include <gui/widgets/Label.hpp>
#include <time/time_conversion.hpp>
#include <time/TimeRangeParser.hpp>

namespace gui
{
    AllEventsItem::AllEventsItem()
    {
        setMinimumSize(style::window::default_body_width, style::window::label::big_h);
        setMargins(gui::Margins(0, style::margins::big, 0, 0));

        hBox = new gui::HBox(this, 0, 0, 0, 0);
        hBox->setEdges(gui::RectangleEdge::None);

        startTime = new gui::Label(hBox, 0, 0, 0, 0);
        startTime->setMinimumSize(style::window::calendar::item::allEvents::start_time_min_w,
                                  style::window::label::big_h);
        startTime->setMaximumSize(style::window::default_body_width, style::window::label::big_h);
        startTime->setEdges(gui::RectangleEdge::None);
        startTime->setFont(style::window::font::small);
        startTime->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        startTime->setMargins(gui::Margins(style::margins::small, 0, 0, 0));

        description = new gui::Label(hBox, 0, 0, 0, 0);
        description->setMinimumSize(style::window::calendar::item::allEvents::description_w,
                                    style::window::label::big_h);
        description->setEdges(gui::RectangleEdge::None);
        description->setFont(style::window::font::bigbold);
        description->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void AllEventsItem::setMarkerItem(UTF8 text)
    {
        hBox->erase();

        auto image = new gui::Image();
        image->set("Rectangle");
        image->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});

        auto dayMonthLabel = new gui::Label();
        dayMonthLabel->setMinimumSize(style::window::calendar::item::allEvents::start_time_min_w,
                                      style::window::label::big_h);
        dayMonthLabel->setMaximumSize(style::window::default_body_width, style::window::label::big_h);
        dayMonthLabel->setEdges(RectangleEdge::None);
        dayMonthLabel->setFont(style::window::font::small);
        dayMonthLabel->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        dayMonthLabel->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        dayMonthLabel->setText(text);

        hBox->addWidget(image);
        hBox->addWidget(dayMonthLabel);
        activeItem = false;
        setEdges(RectangleEdge::None);
    }

    UTF8 AllEventsItem::getLabelMarker() const
    {
        auto startDate = TimePointToYearMonthDay(record->date_from);

        std::string monthStr =
            utils::time::Locale::get_month(utils::time::Locale::Month(unsigned(startDate.month()) - 1));
        return std::to_string(unsigned(startDate.day())) + " " + monthStr;
    }

    void AllEventsItem::setEvent(std::shared_ptr<EventsRecord> rec)
    {
        this->record = rec;
        if (rec != nullptr) {
            description->setText(this->record->title.c_str());
            startTime->setText(utils::time::TimeRangeParser().getCalendarTimeString(
                record->date_from, record->date_till, utils::time::Version::abbrev));
        }
    }
} /* namespace gui */

D module-apps/application-calendar/widgets/AllEventsItem.hpp => module-apps/application-calendar/widgets/AllEventsItem.hpp +0 -29
@@ 1,29 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <ListItem.hpp>
#include <Label.hpp>
#include <BoxLayout.hpp>
#include <module-db/Interface/EventsRecord.hpp>
#include <module-gui/gui/widgets/Image.hpp>

namespace gui
{
    class AllEventsItem : public ListItem
    {
        gui::HBox *hBox         = nullptr;
        gui::Label *startTime   = nullptr;
        gui::Label *description = nullptr;
        std::shared_ptr<EventsRecord> record;

      public:
        AllEventsItem();
        virtual ~AllEventsItem() override = default;

        void setMarkerItem(UTF8 text);
        [[nodiscard]] UTF8 getLabelMarker() const;
        void setEvent(std::shared_ptr<EventsRecord> record);
    };

} /* namespace gui */

D module-apps/application-calendar/widgets/CalendarDateItem.cpp => module-apps/application-calendar/widgets/CalendarDateItem.cpp +0 -30
@@ 1,30 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarDateItem.hpp"
#include <apps-common/widgets/DateAndTimeStyle.hpp>
#include <apps-common/widgets/DateWidget.hpp>
#include <apps-common/widgets/WidgetsUtils.hpp>

namespace date_and_time = style::window::date_and_time;

namespace gui
{
    CalendarDateItem::CalendarDateItem()
    {
        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(Margins(date_and_time::leftMargin, date_and_time::topMargin, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        dateWidget = new DateWidget(vBox);

        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
            dateWidget->loadData(TimePointToYearMonthDay(event->date_from));
        };

        passDefaultCallbacksFromListItem(this, vBox);
    }
} /* namespace gui */

D module-apps/application-calendar/widgets/CalendarDateItem.hpp => module-apps/application-calendar/widgets/CalendarDateItem.hpp +0 -26
@@ 1,26 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "CalendarListItem.hpp"

namespace gui
{
    class DateWidget;
    class VBox;
    class CalendarDateItem : public CalendarListItem
    {
      public:
        CalendarDateItem();

        [[nodiscard]] DateWidget *getDateWidget() const noexcept
        {
            return dateWidget;
        }

      private:
        DateWidget *dateWidget = nullptr;
        VBox *vBox             = nullptr;
    };
} /* namespace gui */

D module-apps/application-calendar/widgets/CalendarListItem.hpp => module-apps/application-calendar/widgets/CalendarListItem.hpp +0 -12
@@ 1,12 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-db/Interface/EventsRecord.hpp>
#include <ListItem.hpp>

namespace gui
{
    using CalendarListItem = ListItemWithCallbacks<EventsRecord>;
} /* namespace gui */

D module-apps/application-calendar/widgets/CalendarListView.cpp => module-apps/application-calendar/widgets/CalendarListView.cpp +0 -123
@@ 1,123 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarListView.hpp"

namespace gui
{

    CalendarListView::CalendarListView(
        Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h, std::shared_ptr<ListItemProvider> prov)
        : ListView(parent, x, y, w, h, prov)
    {
        assert(prov != nullptr);
    }

    void CalendarListView::addLabelMarker(gui::AllEventsItem *item)
    {
        assert(item != nullptr);
        if (direction == listview::Direction::Bottom) {
            if (!(labelMark == item->getLabelMarker())) {

                labelMark = item->getLabelMarker();

                auto *calendarAllEventsItem = new gui::AllEventsItem();
                calendarAllEventsItem->setMarkerItem(labelMark);

                body->addWidget(calendarAllEventsItem);
            }
        }
        if (direction == listview::Direction::Top) {

            if (currentPageSize == 0) {
                labelMark = item->getLabelMarker();
                return;
            }
            else if (!(labelMark == item->getLabelMarker())) {

                previousLabelMark = labelMark;
                labelMark         = item->getLabelMarker();

                auto *calendarAllEventsItem = new gui::AllEventsItem();
                calendarAllEventsItem->setMarkerItem(previousLabelMark);

                body->removeWidget(item);
                body->addWidget(calendarAllEventsItem);
                body->addWidget(item);

                previousItemIsLabel = true;
            }
        }
    }

    void CalendarListView::addItemsOnPage()
    {
        currentPageSize            = 0;
        ListItem *item             = nullptr;
        ListItem *previousListItem = nullptr;
        labelMark                  = "";
        previousLabelMark          = "";

        while ((item = provider->getItem(getOrderFromDirection())) != nullptr) {

            // if direction bot add label mark before adding item
            if (direction == listview::Direction::Bottom) {
                auto eventItem = dynamic_cast<gui::AllEventsItem *>(item);
                if (eventItem != nullptr) {
                    addLabelMarker(eventItem);
                }
            }

            body->addWidget(item);

            // if added item is not visible -> page is full.
            if (!item->visible) {

                // if page full and direction top remove last element and add floating label mark on top if there was no
                // one previously
                if (direction == listview::Direction::Top) {

                    if (previousItemIsLabel) {
                        break;
                    }
                    else {

                        body->erase(item);
                        body->erase(previousListItem);

                        auto *calendarAllEventsItem = new gui::AllEventsItem();

                        calendarAllEventsItem->setMarkerItem(labelMark);

                        body->addWidget(calendarAllEventsItem);
                        currentPageSize--;
                    }
                }
                break;
            }

            // if direction top add label mark after adding item
            if (direction == listview::Direction::Top) {
                previousItemIsLabel = false;
                auto eventItem      = dynamic_cast<gui::AllEventsItem *>(item);
                if (eventItem != nullptr) {
                    addLabelMarker(eventItem);
                }
            }

            previousListItem = item;

            currentPageSize++;
        }

        recalculateStartIndex();

        // Add element on top for first page purpose
        if (startIndex == 0 && direction == listview::Direction::Top) {
            auto *calendarAllEventsItem = new gui::AllEventsItem();
            calendarAllEventsItem->setMarkerItem(labelMark);
            body->addWidget(calendarAllEventsItem);
        }
    }

} /* namespace gui */

D module-apps/application-calendar/widgets/CalendarListView.hpp => module-apps/application-calendar/widgets/CalendarListView.hpp +0 -30
@@ 1,30 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "AllEventsItem.hpp"
#include "Application.hpp"
#include <ListView.hpp>
#include <functional>

namespace gui
{
    class ListItemProvider;

    class CalendarListView : public ListView
    {

        UTF8 labelMark           = "";
        UTF8 previousLabelMark   = "";
        bool previousItemIsLabel = false;

        void addItemsOnPage() override;
        void addLabelMarker(AllEventsItem *item);

      public:
        CalendarListView(
            Item *parent, uint32_t x, uint32_t y, uint32_t w, uint32_t h, std::shared_ptr<ListItemProvider> prov);
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/CalendarStyle.hpp => module-apps/application-calendar/widgets/CalendarStyle.hpp +19 -117
@@ 5,123 5,25 @@
#include <Style.hpp>
#include <string>

namespace style
namespace style::window::calendar
{
    namespace window
    {
        namespace calendar
        {
            namespace imageCircleTop
            {
                inline constexpr auto x    = 116U;
                inline constexpr auto y    = 59U;
                inline constexpr auto name = "circle_top";
            } // namespace imageCircleTop
            namespace imageCircleBottom
            {
                inline constexpr auto x    = 106U;
                inline constexpr auto y    = 240U;
                inline constexpr auto name = "circle_bottom";
            } // namespace imageCircleBottom

            namespace name
            {
                inline constexpr auto day_events_window     = "DayEventsWindow";
                inline constexpr auto no_events_window      = "NoEventsWindow";
                inline constexpr auto events_options        = "Options";
                inline constexpr auto dialog_yes_no         = "DialogYesNo";
                inline constexpr auto all_events_window     = "AllEventsWindow";
                inline constexpr auto details_window        = "DetailsWindow";
                inline constexpr auto new_edit_event        = "NewEditEvent";
                inline constexpr auto custom_repeat_window  = "CustomRepeat";
                inline constexpr auto event_reminder_window = "EventReminderWindow";
            } // namespace name

            inline constexpr auto new_event         = "New";
            inline constexpr auto edit_event        = "Edit";
            inline constexpr auto day_cell_width    = 60;
            inline constexpr auto day_cell_height   = 55;
            inline constexpr auto month_year_height = 60;
            inline constexpr auto week_days_number  = 7;
            inline constexpr auto max_weeks_number  = 6;
            inline constexpr auto leftMargin        = 10;

            inline constexpr auto listView_x = style::window::default_left_margin;
            inline constexpr auto listView_y = style::window::default_vertical_pos;
            inline constexpr auto listView_w = style::listview::body_width_with_scroll;
            inline constexpr auto listView_h = style::window_height - listView_y - style::footer::height;

            namespace test
            {
                inline constexpr auto prev_month_id = 1;
                inline constexpr auto month_id      = 2;
                inline constexpr auto next_month_id = 3;
                inline constexpr auto date_text_1   = "January 2019";
                inline constexpr auto date_text_2   = "February 2019";
                inline constexpr auto date_text_3   = "March 2019";
            } // namespace test

            namespace item
            {
                namespace dayEvents
                {
                    inline constexpr auto title_w    = 255;
                    inline constexpr auto box_height = style::window::label::small_h;
                    inline constexpr auto height     = 100;
                } // namespace dayEvents
    inline constexpr auto day_cell_width    = 60;
    inline constexpr auto day_cell_height   = 55;
    inline constexpr auto month_year_height = 60;
    inline constexpr auto week_days_number  = 7;

                namespace allEvents
                {
                    inline constexpr auto description_w    = 310;
                    inline constexpr auto start_time_min_w = 60;
                } // namespace allEvents
    inline constexpr auto listView_x = style::window::default_left_margin;
    inline constexpr auto listView_y = style::window::default_vertical_pos;
    inline constexpr auto listView_w = style::listview::body_width_with_scroll;
    inline constexpr auto listView_h = style::window_height - listView_y - style::footer::height;

                namespace repeatAndReminder
                {
                    inline constexpr auto height             = 150;
                    inline constexpr auto title_label_h      = 45;
                    inline constexpr auto title_label_margin = 15;
                    inline constexpr auto description_w      = style::window::default_body_width / 2 - title_label_h;
                    inline constexpr auto description_h      = 30;
                } // namespace repeatAndReminder

                namespace eventDetail
                {
                    inline constexpr auto height_min        = 90;
                    inline constexpr auto height_max        = 155;
                    inline constexpr auto margin_top        = 2 * style::margins::big;
                    inline constexpr auto event_time_margin = 25;
                    inline constexpr auto title_h           = 20;
                    inline constexpr auto label_h           = 35;
                } // namespace eventDetail

                namespace checkBox
                {
                    inline constexpr auto height              = 44;
                    inline constexpr auto margin_top          = 18;
                    inline constexpr auto input_box_label_w   = style::window::label::big_h;
                    inline constexpr auto description_label_w = 280;
                } // namespace checkBox

                namespace severalOptions
                {
                    inline constexpr auto height    = 63;
                    inline constexpr auto label_h   = 20;
                    inline constexpr auto arrow_w_h = 12;
                    inline constexpr auto margin    = 5;
                    inline constexpr auto hBox_h    = height - label_h - margin;
                    inline constexpr auto option_w =
                        style::window::default_body_width - 2 * arrow_w_h - 2 * style::window::calendar::leftMargin;
                } // namespace severalOptions

                namespace textWithLabel
                {
                    inline constexpr auto height        = 63;
                    inline constexpr auto description_h = 20;
                    inline constexpr auto text_input_h  = 37;
                    inline constexpr auto margin        = 6;
                } // namespace textWithLabel
            }     // namespace item
        };        // namespace calendar
    }             // namespace window
} // namespace style
    namespace item
    {
        namespace dayEvents
        {
            inline constexpr auto title_w    = 255;
            inline constexpr auto box_height = style::window::label::small_h;
            inline constexpr auto height     = 100;
        } // namespace dayEvents
    }     // namespace item
} // namespace style::window::calendar

D module-apps/application-calendar/widgets/CalendarTimeItem.cpp => module-apps/application-calendar/widgets/CalendarTimeItem.cpp +0 -69
@@ 1,69 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarDateItem.hpp"
#include "CalendarTimeItem.hpp"
#include <apps-common/widgets/DateAndTimeStyle.hpp>
#include <apps-common/widgets/WidgetsUtils.hpp>
#include <module-gui/gui/input/InputEvent.hpp>

namespace date_and_time = style::window::date_and_time;

namespace gui
{
    CalendarTimeItem::CalendarTimeItem(const std::string &description,
                                       TimeWidget::Type type,
                                       const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode,
                                       const std::function<void()> &bottomBarRestoreFromTemporaryMode,
                                       TimePoint dateFrom,
                                       TimePoint dateTill)
    {
        setMinimumSize(style::window::default_body_width, date_and_time::height);
        setEdges(RectangleEdge::None);
        setMargins(gui::Margins(date_and_time::leftMargin, date_and_time::topMargin, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        timeWidget = new TimeWidget(vBox, description, type, bottomBarTemporaryMode, bottomBarRestoreFromTemporaryMode);
        timeWidget->loadData(std::chrono::hours(TimePointToHour24H(dateFrom)),
                             std::chrono::minutes(TimePointToMin(dateFrom)),
                             std::chrono::hours(TimePointToHour24H(dateTill)),
                             std::chrono::minutes(TimePointToMin(dateTill)));

        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
            timeWidget->loadData(std::chrono::hours(TimePointToHour24H(event->date_from)),
                                 std::chrono::minutes(TimePointToMin(event->date_from)),
                                 std::chrono::hours(TimePointToHour24H(event->date_till)),
                                 std::chrono::minutes(TimePointToMin(event->date_till)));
        };

        onSaveCallback = [&](std::shared_ptr<EventsRecord> record) -> bool {
            auto fromTillDate = std::make_shared<utils::time::FromTillDate>(record->date_from, record->date_till);
            if (timeWidget->saveData(fromTillDate)) {
                record->date_from = fromTillDate->from;
                record->date_till = fromTillDate->till;
                return true;
            }
            return false;
        };
        passDefaultCallbacksFromListItem(this, vBox);
    }

    auto CalendarTimeItem::getFromTillDate() const -> std::shared_ptr<utils::time::FromTillDate>
    {
        auto fromTillDate = std::make_shared<utils::time::FromTillDate>();
        timeWidget->saveData(fromTillDate);
        return fromTillDate;
    }

    void CalendarTimeItem::setConnectionToSecondItem(CalendarTimeItem *item)
    {
        timeWidget->setConnectionToSecondItem(item->timeWidget);
    }

    void CalendarTimeItem::setConnectionToDateItem(CalendarDateItem *item)
    {
        timeWidget->setConnectionToDateItem(item->getDateWidget());
    }
} /* namespace gui */

D module-apps/application-calendar/widgets/CalendarTimeItem.hpp => module-apps/application-calendar/widgets/CalendarTimeItem.hpp +0 -30
@@ 1,30 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "CalendarListItem.hpp"
#include <apps-common/widgets/TimeWidget.hpp>

namespace gui
{
    class VBox;
    class CalendarTimeItem : public CalendarListItem
    {
      public:
        CalendarTimeItem(const std::string &description,
                         TimeWidget::Type type,
                         const std::function<void(const UTF8 &text)> &bottomBarTemporaryMode,
                         const std::function<void()> &bottomBarRestoreFromTemporaryMode,
                         TimePoint dateFrom,
                         TimePoint dateTill);

        void setConnectionToSecondItem(CalendarTimeItem *item);
        void setConnectionToDateItem(CalendarDateItem *item);
        auto getFromTillDate() const -> std::shared_ptr<utils::time::FromTillDate>;

      private:
        TimeWidget *timeWidget = nullptr;
        VBox *vBox             = nullptr;
    };
} /* namespace gui */

D module-apps/application-calendar/widgets/CheckBoxWithLabelItem.cpp => module-apps/application-calendar/widgets/CheckBoxWithLabelItem.cpp +0 -116
@@ 1,116 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CheckBoxWithLabelItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "windows/AppWindow.hpp"
#include <ListView.hpp>
#include <Style.hpp>
#include <Utils.hpp>
#include <InputEvent.hpp>

namespace gui
{

    CheckBoxWithLabelItem::CheckBoxWithLabelItem(app::Application *application,
                                                 const std::string &description,
                                                 std::shared_ptr<WeekDaysRepeatData> data)
        : app(application), checkBoxData(std::move(data))
    {
        app = application;
        assert(app != nullptr);

        setMinimumSize(style::window::default_body_width, style::window::calendar::item::checkBox::height);
        setMargins(gui::Margins(
            style::window::calendar::leftMargin, style::window::calendar::item::checkBox::margin_top, 0, 0));
        setEdges(RectangleEdge::None);

        hBox = new gui::HBox(this, 0, 0, 0, 0);
        hBox->setEdges(gui::RectangleEdge::None);

        checkBox = new gui::CheckBox(
            hBox,
            0,
            0,
            0,
            0,
            [=](const UTF8 &text) {
                app->getCurrentWindow()->bottomBarTemporaryMode(text, BottomBar::Side::LEFT, false);
            },
            [=]() { app->getCurrentWindow()->bottomBarRestoreFromTemporaryMode(); });

        checkBox->setMinimumSize(style::window::calendar::item::checkBox::input_box_label_w,
                                 style::window::calendar::item::checkBox::height);
        checkBox->activeItem = false;

        descriptionLabel = new gui::Label(hBox, 0, 0, 0, 0);
        descriptionLabel->setMinimumSize(style::window::calendar::item::checkBox::description_label_w,
                                         style::window::calendar::item::checkBox::height);
        descriptionLabel->setMargins(gui::Margins(style::margins::very_big, 0, 0, 0));
        descriptionLabel->setEdges(gui::RectangleEdge::None);
        descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        descriptionLabel->setFont(style::window::font::medium);
        descriptionLabel->activeItem = false;
        descriptionLabel->setText(description);

        applyCallbacks();
        if (checkBoxData != nullptr) {
            setCheckBoxes();
        }
        onContentChangedCallback = [&]() { return checkBox->isChecked(); };
    }

    void CheckBoxWithLabelItem::applyCallbacks()
    {
        focusChangedCallback = [&](Item &item) {
            if (focus) {
                descriptionLabel->setFont(style::window::font::mediumbold);
                setFocusItem(checkBox);
            }
            else {
                descriptionLabel->setFont(style::window::font::medium);
            }
            return true;
        };

        inputCallback = [&](gui::Item &item, const gui::InputEvent &event) {
            if (checkBox->onInput(event)) {
                checkBox->resizeItems();
                onContentChangedCallback = [&]() { return checkBox->isChecked(); };
                return true;
            }
            return false;
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void CheckBoxWithLabelItem::setCheckBoxes()
    {
        if (descriptionLabel->getText() == utils::translate(style::strings::common::Monday)) {
            checkBox->setImageVisible(checkBoxData->getData(date::Monday.iso_encoding() - 1));
        }
        else if (descriptionLabel->getText() == utils::translate(style::strings::common::Tuesday)) {
            checkBox->setImageVisible(checkBoxData->getData(date::Tuesday.iso_encoding() - 1));
        }
        else if (descriptionLabel->getText() == utils::translate(style::strings::common::Wednesday)) {
            checkBox->setImageVisible(checkBoxData->getData(date::Wednesday.iso_encoding() - 1));
        }
        else if (descriptionLabel->getText() == utils::translate(style::strings::common::Thursday)) {
            checkBox->setImageVisible(checkBoxData->getData(date::Thursday.iso_encoding() - 1));
        }
        else if (descriptionLabel->getText() == utils::translate(style::strings::common::Friday)) {
            checkBox->setImageVisible(checkBoxData->getData(date::Friday.iso_encoding() - 1));
        }
        else if (descriptionLabel->getText() == utils::translate(style::strings::common::Saturday)) {
            checkBox->setImageVisible(checkBoxData->getData(date::Saturday.iso_encoding() - 1));
        }
        else if (descriptionLabel->getText() == utils::translate(style::strings::common::Sunday)) {
            checkBox->setImageVisible(checkBoxData->getData(date::Sunday.iso_encoding() - 1));
        }
    }

} /* namespace gui */

D module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp => module-apps/application-calendar/widgets/CheckBoxWithLabelItem.hpp +0 -36
@@ 1,36 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "CalendarListItem.hpp"
#include "Application.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include <Label.hpp>
#include <Image.hpp>
#include <BoxLayout.hpp>
#include <BottomBar.hpp>
#include <CheckBox.hpp>

namespace gui
{
    class CheckBoxWithLabelItem : public CalendarListItem
    {
        gui::HBox *hBox           = nullptr;
        app::Application *app     = nullptr;
        std::shared_ptr<WeekDaysRepeatData> checkBoxData = nullptr;

        virtual void applyCallbacks();
        void setCheckBoxes();

      public:
        CheckBoxWithLabelItem(app::Application *application,
                              const std::string &description,
                              std::shared_ptr<WeekDaysRepeatData> data = nullptr);
        virtual ~CheckBoxWithLabelItem() override = default;

        gui::Label *descriptionLabel = nullptr;
        gui::CheckBox *checkBox      = nullptr;
    };

} /* namespace gui */

D module-apps/application-calendar/widgets/DayEventsItem.cpp => module-apps/application-calendar/widgets/DayEventsItem.cpp +0 -63
@@ 1,63 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DayEventsItem.hpp"
#include "CalendarStyle.hpp"
#include <ListView.hpp>
#include <gui/widgets/Label.hpp>
#include <Style.hpp>
#include <time/TimeRangeParser.hpp>

namespace gui
{

    DayEventsItem::DayEventsItem()
    {
        setMinimumSize(style::window::default_body_width, style::window::calendar::item::dayEvents::height);
        setMargins(gui::Margins(0, style::margins::small, 0, 0));

        vBox = new gui::VBox(this, 0, 0, 0, 0);
        vBox->setEdges(gui::RectangleEdge::None);
        hBox = new gui::HBox(vBox, 0, 0, 0, 0);
        hBox->setMinimumSize(style::window::default_body_width, style::window::calendar::item::dayEvents::box_height);
        hBox->setMargins(gui::Margins(0, style::margins::small + style::margins::big, 0, 0));
        hBox->setEdges(gui::RectangleEdge::None);

        title = new gui::Label(hBox, 0, 0, 0, 0);
        title->setMinimumSize(style::window::calendar::item::dayEvents::title_w,
                              style::window::calendar::item::dayEvents::box_height);
        title->setMargins(gui::Margins(style::margins::small, 0, style::margins::small + style::margins::big, 0));
        title->setEdges(gui::RectangleEdge::None);
        title->setFont(style::window::font::bigbold);
        title->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});

        clock = new gui::Image(hBox, 0, 0, 0, 0, "small_tick_W_M");
        clock->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});

        description = new gui::Label(vBox, 0, 0, 0, 0);
        description->setMinimumSize(style::window::default_body_width,
                                    style::window::calendar::item::dayEvents::box_height);
        description->setMargins(gui::Margins(style::margins::small, style::margins::big, 0, style::margins::very_big));
        description->setEdges(gui::RectangleEdge::None);
        description->setFont(style::window::font::medium);
        description->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }

    void DayEventsItem::setEvent(std::shared_ptr<EventsRecord> rec)
    {
        this->record = rec;

        if (rec != nullptr) {
            description->setText(this->record->title.c_str());
            title->setText(utils::time::TimeRangeParser().getCalendarTimeString(record->date_from, record->date_till));
            if (record->reminder == static_cast<uint32_t>(Reminder::never)) {
                clock->setVisible(false);
            }
        }
    }
} /* namespace gui */

D module-apps/application-calendar/widgets/DayEventsItem.hpp => module-apps/application-calendar/widgets/DayEventsItem.hpp +0 -32
@@ 1,32 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include <Label.hpp>
#include <ListItem.hpp>
#include <module-db/Interface/EventsRecord.hpp>
#include <BoxLayout.hpp>
#include <module-gui/gui/widgets/Image.hpp>
#include <Image.hpp>

namespace gui

{
    class DayEventsItem : public ListItem
    {
        gui::VBox *vBox         = nullptr;
        gui::HBox *hBox         = nullptr;
        gui::Label *title       = nullptr;
        gui::Label *description = nullptr;
        gui::Image *clock       = nullptr;

      public:
        std::shared_ptr<EventsRecord> record;
        DayEventsItem();
        virtual ~DayEventsItem() override = default;

        // virtual methods from Item
        void setEvent(std::shared_ptr<EventsRecord> record);
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/DayLabel.cpp => module-apps/application-calendar/widgets/DayLabel.cpp +1 -12
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DayLabel.hpp"


@@ 70,17 70,6 @@ namespace gui
                this->dayNumber->setFont(style::window::font::medium);
            }
            this->activatedCallback = [=](gui::Item &item) {
                auto data       = std::make_unique<DayMonthData>();
                auto month      = actualMonthBox->month;
                auto dateFilter = actualMonthBox->monthFilterValue.year() / actualMonthBox->monthFilterValue.month() /
                                  date::day(numb);
                auto filter = TimePointFromYearMonthDay(dateFilter);
                data->setData(number + " " + month, filter);
                LOG_DEBUG("Switch to DayEventsWindow");
                if (auto calendarApp = dynamic_cast<app::ApplicationCalendar *>(app); calendarApp != nullptr) {
                    calendarApp->setEquivalentToEmptyWindow(EquivalentWindow::DayEventsWindow);
                }
                app->switchWindow(style::window::calendar::name::day_events_window, std::move(data));
                return true;
            };
            this->setPenWidth(style::window::default_border_no_focus_w);

D module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp => module-apps/application-calendar/widgets/EventDetailDescriptionItem.cpp +0 -85
@@ 1,85 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventDetailDescriptionItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include <Style.hpp>
#include <time/time_conversion.hpp>
#include <date/date.h>
#include <time/TimeRangeParser.hpp>

namespace gui
{

    EventDetailDescriptionItem::EventDetailDescriptionItem()
    {
        setEdges(RectangleEdge::None);
        setMinimumSize(style::window::default_body_width, style::window::calendar::item::eventDetail::height_min);
        setMaximumSize(style::window::default_body_width, style::window::calendar::item::eventDetail::height_max);
        setMargins(gui::Margins(style::margins::small, style::window::calendar::item::eventDetail::margin_top, 0, 0));

        vBox = new VBox(this, 0, 0, 0, 0);
        vBox->setEdges(RectangleEdge::None);

        hBox = new HBox(vBox, 0, 0, 0, 0);
        hBox->setMinimumSize(style::window::default_body_width, style::window::calendar::item::eventDetail::title_h);
        hBox->setEdges(RectangleEdge::None);

        squareImage = new gui::Image();
        squareImage->set("Rectangle");
        squareImage->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        hBox->addWidget(squareImage);

        title = new gui::Label(hBox, 0, 0, 0, 0);
        title->setMinimumSize(style::window::default_body_width - 40,
                              style::window::calendar::item::eventDetail::title_h);
        title->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        title->setEdges(RectangleEdge::None);
        title->setFont(style::window::font::small);
        title->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        title->activeItem = false;

        eventTime = new gui::Label(vBox, 0, 0, 0, 0);
        eventTime->setMinimumSize(style::window::default_body_width,
                                  style::window::calendar::item::eventDetail::label_h);
        eventTime->setEdges(RectangleEdge::None);
        eventTime->setMargins(
            gui::Margins(0, style::window::calendar::item::eventDetail::event_time_margin, 0, style::margins::small));
        eventTime->setFont(style::window::font::bigbold);
        eventTime->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        eventTime->activeItem = false;

        description = new gui::Text(vBox, 0, 0, 0, 0);
        description->setMinimumSize(style::window::default_body_width,
                                    style::window::calendar::item::eventDetail::label_h);
        description->setEdges(RectangleEdge::None);
        description->setMaximumSize(style::window::default_body_width,
                                    2 * style::window::calendar::item::eventDetail::label_h);
        description->setFont(style::window::font::big);
        description->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        description->setEditMode(EditMode::Browse);

        focusChangedCallback = [&](Item &item) {
            setFocusItem(focus ? vBox : nullptr);
            return true;
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

        descriptionHandler();
    }

    void EventDetailDescriptionItem::descriptionHandler()
    {
        title->setText(utils::translate("app_calendar_event_detail"));
        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
            description->setText(event->title);
            eventTime->setText(
                utils::time::TimeRangeParser().getCalendarTimeString(event->date_from, event->date_till));
        };
    }

} /* namespace gui */

D module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp => module-apps/application-calendar/widgets/EventDetailDescriptionItem.hpp +0 -29
@@ 1,29 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>
#include <module-gui/gui/widgets/Image.hpp>

namespace gui
{
    class EventDetailDescriptionItem : public CalendarListItem
    {
        gui::VBox *vBox        = nullptr;
        gui::HBox *hBox         = nullptr;
        gui::Label *title      = nullptr;
        gui::Label *eventTime  = nullptr;
        gui::Text *description = nullptr;
        gui::Image *squareImage = nullptr;

      public:
        EventDetailDescriptionItem();
        virtual ~EventDetailDescriptionItem() override = default;

        void descriptionHandler();
    };

} /* namespace gui */

M module-apps/application-calendar/widgets/MonthBox.cpp => module-apps/application-calendar/widgets/MonthBox.cpp +4 -23
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "MonthBox.hpp"


@@ 19,8 19,6 @@ namespace gui
                       std::array<bool, 31> &isDayEmpty)
        : GridLayout(parent, style::window::default_left_margin, offsetTop, width, height, {dayWidth, dayHeight})
    {
        LOG_DEBUG("Call MonthBox constructor");

        assert(parent);
        parent->addWidget(this);
        month            = model->getMonthText();


@@ 55,29 53,12 @@ namespace gui
                addWidget(day);
            }
        }
        auto mainWindow = dynamic_cast<CalendarMainWindow *>(parent);
        if (mainWindow->returnedFromWindow) {
        date::year_month_day actualDate = TimePointToYearMonthDay(TimePointNow());
        if (model->getYear() == actualDate.year() && model->getMonth() == actualDate.month()) {
            focusChangedCallback = [=](Item &item) {
                calendar::YearMonthDay date = monthFilterValue.year() / monthFilterValue.month() / date::last;
                if (unsigned(date.day()) < mainWindow->dayFocusedBefore) {
                    setFocusOnElement(unsigned(date.day()) - 1);
                }
                else {
                    setFocusOnElement(mainWindow->dayFocusedBefore - 1);
                }
                setFocusOnElement(unsigned(actualDate.day()) - 1);
                return true;
            };
        }
        else {
            date::year_month_day actualDate = TimePointToYearMonthDay(TimePointNow());
            if (model->getYear() == actualDate.year() && model->getMonth() == actualDate.month()) {
                focusChangedCallback = [=](Item &item) {
                    setFocusOnElement(unsigned(actualDate.day()) - 1);
                    return true;
                };
            }
        }

        LOG_DEBUG("MonthBox constructor Completed Successfully!");
    }
} /* namespace gui */

D module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.cpp => module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.cpp +0 -63
@@ 1,63 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NewEventCheckBoxWithLabel.hpp"
#include "AppWindow.hpp"
#include "InputEvent.hpp"

namespace gui
{

    NewEventCheckBoxWithLabel::NewEventCheckBoxWithLabel(app::Application *application,
                                                         const std::string &description,
                                                         OnCheckCallback onCheck)
        : CheckBoxWithLabelItem(application, description, nullptr), onCheck(std::move(onCheck))
    {
        app = application;
        assert(app != nullptr);

        setMargins(gui::Margins(style::margins::small,
                                style::window::calendar::item::checkBox::margin_top,
                                0,
                                style::window::calendar::leftMargin));
        applyCallbacks();
    }

    void NewEventCheckBoxWithLabel::applyCallbacks()
    {
        inputCallback = [this](gui::Item &item, const gui::InputEvent &event) {
            if (event.isShortRelease(gui::KeyCode::KEY_LF)) {
                onCheck(checkBox->isChecked());
            }
            return checkBox->onInput(event);
        };

        onLoadCallback = [this](std::shared_ptr<EventsRecord> event) {
            if (allDayEvents::isAllDayEvent(event->date_from, event->date_till)) {
                checkBox->setImageVisible(true);
            }
        };
        onSaveCallback = [this](std::shared_ptr<EventsRecord> event) {
            if (checkBox->isChecked()) {
                event->date_from = TimePointFromYearMonthDay(dateItem->getChosenDate());
                event->date_till = event->date_from + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                                   std::chrono::minutes(utils::time::Locale::max_minutes);
            }
        };
    }

    void NewEventCheckBoxWithLabel::setConnectionToDateItem(gui::DateWidget *item)
    {
        dateItem = item;
    }

    bool allDayEvents::isAllDayEvent(TimePoint start, TimePoint end)
    {
        auto startTime = TimePointToHourMinSec(start);
        auto endTime   = TimePointToHourMinSec(end);
        return startTime.hours().count() == 0 && startTime.minutes().count() == 0 &&
               endTime.hours().count() == utils::time::Locale::max_hour_24H_mode &&
               endTime.minutes().count() == utils::time::Locale::max_minutes;
    }

} /* namespace gui */

D module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.hpp => module-apps/application-calendar/widgets/NewEventCheckBoxWithLabel.hpp +0 -31
@@ 1,31 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "CheckBoxWithLabelItem.hpp"
#include <widgets/DateWidget.hpp>

namespace gui
{
    class NewEventCheckBoxWithLabel : public CheckBoxWithLabelItem
    {
        using OnCheckCallback     = std::function<void(bool)>;
        OnCheckCallback onCheck   = nullptr;
        app::Application *app    = nullptr;
        gui::DateWidget *dateItem = nullptr;
        void applyCallbacks() override;

      public:
        NewEventCheckBoxWithLabel(app::Application *application,
                                  const std::string &description,
                                  OnCheckCallback onCheck);

        void setConnectionToDateItem(gui::DateWidget *item);
    };

    namespace allDayEvents
    {
        bool isAllDayEvent(TimePoint start, TimePoint end);
    }

} /* namespace gui */

D module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp => module-apps/application-calendar/widgets/RepeatAndReminderItem.cpp +0 -118
@@ 1,118 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "RepeatAndReminderItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include "application-alarm-clock/data/AlarmsData.hpp"
#include <Style.hpp>
#include <Utils.hpp>
#include <Image.hpp>

namespace gui
{

    RepeatAndReminderItem::RepeatAndReminderItem(app::ApplicationCalendar *application) : app(application)
    {
        activeItem = false;
        setEdges(RectangleEdge::None);
        setMinimumSize(style::window::default_body_width, style::window::calendar::item::repeatAndReminder::height);
        setMargins(gui::Margins(style::margins::small, 0, 0, 0));

        std::vector<gui::Image *> squareImages;
        for (int i = 0; i < 2; i++) {
            auto square = new gui::Image();
            square->set("Rectangle");
            square->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
            squareImages.push_back(square);
        }

        hBox = new HBox(this, 0, 0, 0, 0);
        hBox->setEdges(RectangleEdge::None);

        repeatVBox = new VBox(hBox, 0, 0, 0, 0);
        repeatVBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                   style::window::calendar::item::repeatAndReminder::height);
        repeatVBox->setEdges(RectangleEdge::None);

        reminderVBox = new VBox(hBox, 0, 0, 0, 0);
        reminderVBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                     style::window::calendar::item::repeatAndReminder::height);
        reminderVBox->setEdges(RectangleEdge::None);

        repeatHBox = new HBox(repeatVBox, 0, 0, 0, 0);
        repeatHBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                   style::window::calendar::item::repeatAndReminder::title_label_h);
        repeatHBox->setMargins(
            gui::Margins(0, 0, 0, style::window::calendar::item::repeatAndReminder::title_label_margin));
        repeatHBox->setEdges(RectangleEdge::None);
        repeatHBox->addWidget(squareImages[0]);

        repeatTitle = new gui::Label(repeatHBox, 0, 0, 0, 0);
        repeatTitle->setMinimumSize(style::window::calendar::item::repeatAndReminder::description_w,
                                    style::window::calendar::item::repeatAndReminder::title_label_h);
        repeatTitle->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        repeatTitle->setEdges(RectangleEdge::None);
        repeatTitle->setFont(style::window::font::small);
        repeatTitle->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        repeatTitle->activeItem = false;

        repeat = new gui::Label(repeatVBox, 0, 0, 0, 0);
        repeat->setMinimumSize(style::window::calendar::item::repeatAndReminder::description_w,
                               style::window::calendar::item::repeatAndReminder::description_h);
        repeat->setEdges(RectangleEdge::None);
        repeat->setFont(style::window::font::medium);
        repeat->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        repeat->activeItem = false;

        reminderHBox = new HBox(reminderVBox, 0, 0, 0, 0);
        reminderHBox->setMinimumSize(style::window::default_body_width / 2 - style::margins::small / 2,
                                     style::window::calendar::item::repeatAndReminder::title_label_h);
        reminderHBox->setMargins(
            gui::Margins(0, 0, 0, style::window::calendar::item::repeatAndReminder::title_label_margin));
        reminderHBox->setEdges(RectangleEdge::None);
        reminderHBox->addWidget(squareImages[1]);

        reminderTitle = new gui::Label(reminderHBox, 0, 0, 0, 0);
        reminderTitle->setMinimumSize(style::window::calendar::item::repeatAndReminder::description_w,
                                      style::window::calendar::item::repeatAndReminder::title_label_h);
        reminderTitle->setMargins(gui::Margins(style::margins::small, 0, 0, 0));
        reminderTitle->setEdges(RectangleEdge::None);
        reminderTitle->setFont(style::window::font::small);
        reminderTitle->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        reminderTitle->activeItem = false;

        reminder = new gui::Label(reminderVBox, 0, 0, 0, 0);
        reminder->setMinimumSize(style::window::default_body_width / 2,
                                 style::window::calendar::item::repeatAndReminder::description_h);
        reminder->setEdges(RectangleEdge::None);
        reminder->setFont(style::window::font::medium);
        reminder->setAlignment(gui::Alignment{gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center});
        reminder->activeItem = false;

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            hBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };

        descriptionHandler();
    }

    void RepeatAndReminderItem::descriptionHandler()
    {
        repeatTitle->setText(utils::translate("app_calendar_event_detail_repeat"));
        reminderTitle->setText(utils::translate("app_calendar_event_detail_reminder"));
        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
            if (event->repeat > app::ApplicationCalendar::repeatOptions.size()) {
                repeat->setText(CustomRepeatValueParser(event->repeat).getWeekDaysText());
            }
            else {
                repeat->setText(
                    utils::translate(app::ApplicationCalendar::repeatOptions.at(static_cast<Repeat>(event->repeat))));
            }
            reminder->setText(
                utils::translate(app::ApplicationCalendar::reminderOptions.at(static_cast<Reminder>(event->reminder))));
        };
    }

} /* namespace gui */

D module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp => module-apps/application-calendar/widgets/RepeatAndReminderItem.hpp +0 -36
@@ 1,36 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>
#include <BoxLayout.hpp>
#include <apps-common/Application.hpp>
#include <module-apps/application-calendar/ApplicationCalendar.hpp>

namespace gui

{
    class RepeatAndReminderItem : public CalendarListItem
    {
        gui::HBox *hBox           = nullptr;
        gui::VBox *repeatVBox     = nullptr;
        gui::VBox *reminderVBox   = nullptr;
        gui::HBox *repeatHBox         = nullptr;
        gui::HBox *reminderHBox       = nullptr;
        gui::Label *repeatTitle   = nullptr;
        gui::Label *repeat        = nullptr;
        gui::Label *reminderTitle = nullptr;
        gui::Label *reminder      = nullptr;
        app::ApplicationCalendar *app = nullptr;

      public:
        RepeatAndReminderItem(app::ApplicationCalendar *application);
        virtual ~RepeatAndReminderItem() = default;

        void descriptionHandler();
    };

} /* namespace gui */

D module-apps/application-calendar/widgets/SeveralOptionsItem.cpp => module-apps/application-calendar/widgets/SeveralOptionsItem.cpp +0 -227
@@ 1,227 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "SeveralOptionsItem.hpp"
#include "InputEvent.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include "application-calendar/data/OptionParser.hpp"
#include "application-alarm-clock//data/AlarmsData.hpp"
#include <Style.hpp>
#include <Utils.hpp>
#include <module-db/Interface/EventsRecord.hpp>

namespace gui
{
    namespace optionsItem = style::window::calendar::item::severalOptions;

    SeveralOptionsItem::SeveralOptionsItem(app::Application *app,
                                           const std::string &description,
                                           std::function<void(const UTF8 &)> bottomBarTemporaryMode,
                                           std::function<void()> bottomBarRestoreFromTemporaryMode)
        : bottomBarTemporaryMode(std::move(bottomBarTemporaryMode)),
          bottomBarRestoreFromTemporaryMode(std::move(bottomBarRestoreFromTemporaryMode))
    {
        application = app;
        assert(app != nullptr);

        setMinimumSize(style::window::default_body_width, optionsItem::height);

        setEdges(RectangleEdge::Bottom);
        setPenWidth(style::window::default_border_rect_no_focus);
        setMargins(gui::Margins(style::window::calendar::leftMargin, style::margins::huge, 0, 0));

        vBox = new gui::VBox(this, 0, 0, 0, 0);
        vBox->setEdges(gui::RectangleEdge::None);
        vBox->activeItem = false;

        descriptionLabel = new gui::Label(vBox, 0, 0, 0, 0);
        descriptionLabel->setMinimumSize(style::window::default_body_width, optionsItem::label_h);
        descriptionLabel->setMargins(gui::Margins(0, 0, 0, optionsItem::margin));
        descriptionLabel->setEdges(gui::RectangleEdge::None);
        descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        descriptionLabel->setFont(style::window::font::small);
        descriptionLabel->activeItem = false;
        descriptionLabel->setText(description);

        hBox = new gui::HBox(vBox, 0, 0, 0, 0);
        hBox->setMinimumSize(style::window::default_body_width, optionsItem::hBox_h);
        hBox->setEdges(gui::RectangleEdge::None);
        hBox->activeItem = false;

        leftArrow = new gui::Image(hBox, 0, 0, 0, 0);
        leftArrow->setMinimumSize(optionsItem::arrow_w_h, optionsItem::arrow_w_h);
        leftArrow->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        leftArrow->activeItem = false;
        leftArrow->set("arrow_left");
        leftArrow->setVisible(false);

        optionLabel = new gui::Label(hBox, 0, 0, 0, 0);
        optionLabel->setMinimumSize(optionsItem::option_w, optionsItem::hBox_h);
        optionLabel->setMargins(gui::Margins(optionsItem::arrow_w_h, 0, 0, 0));
        optionLabel->setEdges(gui::RectangleEdge::None);
        optionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        optionLabel->setFont(style::window::font::medium);
        optionLabel->activeItem = false;

        rightArrow = new gui::Image(hBox, 0, 0, 0, 0);
        rightArrow->setMinimumSize(optionsItem::arrow_w_h, optionsItem::arrow_w_h);
        rightArrow->setAlignment(Alignment(gui::Alignment::Horizontal::Right, gui::Alignment::Vertical::Center));
        rightArrow->activeItem = false;
        rightArrow->set("arrow_right");
        rightArrow->setVisible(false);

        prepareOptionsNames();
        applyCallbacks();
    }

    void SeveralOptionsItem::prepareOptionsNames()
    {
        optionsNames.clear();
        if (descriptionLabel->getText() == utils::translate("app_calendar_event_detail_reminder")) {
            optionsNames.push_back(utils::translate("app_calendar_reminder_never"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_event_time"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_5_min_before"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_15_min_before"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_30_min_before"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_1_hour_before"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_2_hour_before"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_1_day_before"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_2_days_before"));
            optionsNames.push_back(utils::translate("app_calendar_reminder_1_week_before"));
        }
        else if (descriptionLabel->getText() == utils::translate("app_calendar_event_detail_repeat")) {
            optionsNames.push_back(utils::translate("app_calendar_repeat_never"));
            optionsNames.push_back(utils::translate("app_calendar_repeat_daily"));
            optionsNames.push_back(utils::translate("app_calendar_repeat_weekly"));
            optionsNames.push_back(utils::translate("app_calendar_repeat_two_weeks"));
            optionsNames.push_back(utils::translate("app_calendar_repeat_month"));
            optionsNames.push_back(utils::translate("app_calendar_repeat_year"));
            optionsNames.push_back(utils::translate("app_calendar_repeat_custom"));
        }
    }

    void SeveralOptionsItem::applyCallbacks()
    {
        focusChangedCallback = [&](Item &item) {
            if (item.focus) {
                if (actualVectorIndex == optionsNames.size() - 1 &&
                    descriptionLabel->getText() == utils::translate("app_calendar_event_detail_repeat")) {
                    bottomBarTemporaryMode(utils::translate("app_calendar_edit"));
                }
                optionLabel->setMargins(gui::Margins(0, 0, 0, 0));
            }
            else {
                optionLabel->setMargins(gui::Margins(optionsItem::arrow_w_h, 0, 0, 0));
                bottomBarRestoreFromTemporaryMode();
            }
            leftArrow->setVisible(item.focus);
            rightArrow->setVisible(item.focus);
            hBox->resizeItems();
            return true;
        };

        inputCallback = [&](gui::Item &item, const gui::InputEvent &event) {
            if (!event.isShortRelease()) {
                return false;
            }
            if (event.is(gui::KeyCode::KEY_LEFT)) {
                actualVectorIndex--;
                if (actualVectorIndex >= optionsNames.size()) {
                    actualVectorIndex = optionsNames.size() - 1;
                    if (descriptionLabel->getText() == utils::translate("app_calendar_event_detail_repeat")) {
                        bottomBarTemporaryMode(utils::translate("app_calendar_edit"));
                    }
                }
                else {
                    bottomBarRestoreFromTemporaryMode();
                }
                optionLabel->setText(optionsNames[actualVectorIndex]);
                return true;
            }
            if (event.is(gui::KeyCode::KEY_RIGHT)) {
                actualVectorIndex++;
                if (actualVectorIndex >= optionsNames.size()) {
                    actualVectorIndex = 0;
                }
                optionLabel->setText(optionsNames[actualVectorIndex]);
                if (actualVectorIndex == optionsNames.size() - 1 &&
                    descriptionLabel->getText() == utils::translate("app_calendar_event_detail_repeat")) {
                    bottomBarTemporaryMode(utils::translate("app_calendar_edit"));
                }
                else {
                    bottomBarRestoreFromTemporaryMode();
                }
                return true;
            }
            if (event.is(gui::KeyCode::KEY_LF) && actualVectorIndex == optionsNames.size() - 1 &&
                descriptionLabel->getText() == utils::translate("app_calendar_event_detail_repeat")) {
                OptionParser parser;
                auto weekDayRepeatData = std::make_unique<WeekDaysRepeatData>();
                assert(weekDayRepeatData != nullptr);
                auto weekDayData = parser.setWeekDayOptions(repeatOptionValue, std::move(weekDayRepeatData));
                application->switchWindow(style::window::calendar::name::custom_repeat_window, std::move(weekDayData));
                return true;
            }
            return false;
        };

        onSaveCallback = [&](std::shared_ptr<EventsRecord> record) {
            if (descriptionLabel->getText() == utils::translate("app_calendar_event_detail_repeat")) {
                if (record->repeat < optionsNames.size() - 1 && actualVectorIndex != optionsNames.size() - 1) {
                    record->repeat = actualVectorIndex;
                }
                else if (record->repeat == optionsNames.size() - 1 ||
                         optionsNames[optionsNames.size() - 1] == utils::translate("app_calendar_repeat_custom")) {
                    record->repeat = static_cast<uint32_t>(AlarmRepeat::never);
                }
            }
            else if (descriptionLabel->getText() == utils::translate("app_calendar_event_detail_reminder")) {
                record->reminder = static_cast<uint32_t>(reminderTimeOptions[actualVectorIndex]);
            }
        };

        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) {
            if (descriptionLabel->getText() == utils::translate("app_calendar_event_detail_repeat")) {
                if (event->repeat < optionsNames.size() - 1) {
                    actualVectorIndex = event->repeat;
                    if (event->repeat == static_cast<uint32_t>(Repeat::never)) {
                        optionsNames[optionsNames.size() - 1] = utils::translate("app_calendar_repeat_custom");
                    }
                    bottomBarRestoreFromTemporaryMode();
                }
                else {
                    auto parser = CustomRepeatValueParser(event->repeat);
                    if (parser.isCustomValueEveryday()) {
                        actualVectorIndex = static_cast<uint32_t>(Repeat::daily);
                        event->repeat     = actualVectorIndex;
                        bottomBarRestoreFromTemporaryMode();
                        optionsNames[optionsNames.size() - 1] = utils::translate("app_calendar_repeat_custom");
                    }
                    else {
                        actualVectorIndex                     = optionsNames.size() - 1;
                        optionsNames[optionsNames.size() - 1] = parser.getWeekDaysText();
                        if (this->focus) {
                            bottomBarTemporaryMode(utils::translate("app_calendar_edit"));
                        }
                    }
                }
                repeatOptionValue = event->repeat;
                optionLabel->setText(optionsNames[actualVectorIndex]);
            }
            else if (descriptionLabel->getText() == utils::translate("app_calendar_event_detail_reminder")) {
                actualVectorIndex = std::find(reminderTimeOptions.begin(),
                                              reminderTimeOptions.end(),
                                              static_cast<Reminder>(event->reminder)) -
                                    reminderTimeOptions.begin();
                optionLabel->setText(optionsNames[actualVectorIndex]);
            }
        };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }
} /* namespace gui */

D module-apps/application-calendar/widgets/SeveralOptionsItem.hpp => module-apps/application-calendar/widgets/SeveralOptionsItem.hpp +0 -55
@@ 1,55 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Application.hpp"
#include "CalendarListItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include <module-apps/application-calendar/data/CalendarData.hpp>
#include <Label.hpp>
#include <Image.hpp>
#include <BoxLayout.hpp>

namespace gui
{
    const std::array<Reminder, 10> reminderTimeOptions = {Reminder::never,
                                                          Reminder::event_time,
                                                          Reminder::five_min_before,
                                                          Reminder::fifteen_min_before,
                                                          Reminder::thirty_min_before,
                                                          Reminder::one_hour_before,
                                                          Reminder::two_hour_before,
                                                          Reminder::one_day_before,
                                                          Reminder::two_days_before,
                                                          Reminder::one_week_before};

    class SeveralOptionsItem : public CalendarListItem
    {
        app::Application *application = nullptr;
        gui::VBox *vBox               = nullptr;
        gui::HBox *hBox               = nullptr;
        gui::Label *optionLabel       = nullptr;
        gui::Label *descriptionLabel  = nullptr;
        gui::Image *leftArrow         = nullptr;
        gui::Image *rightArrow        = nullptr;
        std::vector<std::string> optionsNames;

        unsigned int actualVectorIndex = 0;

        std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr;
        std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr;

      public:
        SeveralOptionsItem(app::Application *app,
                           const std::string &description,
                           std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                           std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr);
        virtual ~SeveralOptionsItem() override = default;

        void prepareOptionsNames();
        void applyCallbacks();
        uint32_t repeatOptionValue = 0;
    };

} /* namespace gui */

D module-apps/application-calendar/widgets/TextWithLabelItem.cpp => module-apps/application-calendar/widgets/TextWithLabelItem.cpp +0 -72
@@ 1,72 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "TextWithLabelItem.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include <Style.hpp>

namespace gui
{

    TextWithLabelItem::TextWithLabelItem(const std::string &description,
                                         std::function<void(const UTF8 &)> bottomBarTemporaryMode,
                                         std::function<void()> bottomBarRestoreFromTemporaryMode,
                                         std::function<void()> selectSpecialCharacter)
    {
        setMinimumSize(style::window::default_body_width, style::window::calendar::item::textWithLabel::height);
        setMargins(gui::Margins(style::window::calendar::leftMargin,
                                2 * style::window::calendar::leftMargin,
                                0,
                                1.5 * style::window::calendar::leftMargin));
        setEdges(RectangleEdge::None);
        bottomBarRestoreFromTemporaryMode();

        vBox = new gui::VBox(this, 0, 0, 0, 0);
        vBox->setEdges(gui::RectangleEdge::None);
        vBox->setPenFocusWidth(style::window::default_border_focus_w);
        vBox->setPenWidth(style::window::default_border_rect_no_focus);

        descriptionLabel = new gui::Label(vBox, 0, 0, 0, 0);
        descriptionLabel->setMinimumSize(style::window::default_body_width,
                                         style::window::calendar::item::textWithLabel::description_h);
        descriptionLabel->setMargins(gui::Margins(0, 0, 0, style::window::calendar::item::textWithLabel::margin));
        descriptionLabel->setEdges(gui::RectangleEdge::None);
        descriptionLabel->setAlignment(Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Top));
        descriptionLabel->setFont(style::window::font::small);
        descriptionLabel->activeItem = false;
        descriptionLabel->setText(description);

        textInput = new gui::Text(vBox, 0, 0, 0, 0);
        textInput->setMinimumSize(style::window::default_body_width,
                                  style::window::calendar::item::textWithLabel::text_input_h);
        textInput->setEdges(gui::RectangleEdge::Bottom);
        textInput->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Left, gui::Alignment::Vertical::Center));
        textInput->setFont(style::window::font::medium);
        textInput->setInputMode(new InputMode(
            {InputMode::ABC, InputMode::abc, InputMode::digit},
            [=](const UTF8 &text) { bottomBarTemporaryMode(text); },
            [=]() { bottomBarRestoreFromTemporaryMode(); },
            [=]() { selectSpecialCharacter(); }));
        textInput->setPenFocusWidth(style::window::default_border_focus_w);
        textInput->setPenWidth(style::window::default_border_rect_no_focus);
        textInput->setEditMode(gui::EditMode::Edit);

        focusChangedCallback = [&](Item &item) {
            setFocusItem(focus ? vBox : nullptr);
            return true;
        };

        inputCallback = [&](Item &item, const InputEvent &event) {
            auto ret       = textInput->onInput(event);
            onSaveCallback = [&](std::shared_ptr<EventsRecord> record) { record->title = textInput->getText(); };
            return ret;
        };

        onLoadCallback = [&](std::shared_ptr<EventsRecord> event) { textInput->setText(event->title); };

        dimensionChangedCallback = [&](gui::Item &, const BoundingBox &newDim) -> bool {
            vBox->setArea({0, 0, newDim.w, newDim.h});
            return true;
        };
    }
} /* namespace gui */

D module-apps/application-calendar/widgets/TextWithLabelItem.hpp => module-apps/application-calendar/widgets/TextWithLabelItem.hpp +0 -27
@@ 1,27 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "CalendarListItem.hpp"
#include <Label.hpp>
#include <Text.hpp>
#include <BoxLayout.hpp>

namespace gui
{
    class TextWithLabelItem : public CalendarListItem
    {
        gui::VBox *vBox              = nullptr;
        gui::Label *descriptionLabel = nullptr;
        gui::Text *textInput         = nullptr;

      public:
        TextWithLabelItem(const std::string &description,
                          std::function<void(const UTF8 &text)> bottomBarTemporaryMode = nullptr,
                          std::function<void()> bottomBarRestoreFromTemporaryMode      = nullptr,
                          std::function<void()> selectSpecialCharacter                 = nullptr);
        virtual ~TextWithLabelItem() override = default;
    };

} /* namespace gui */

D module-apps/application-calendar/windows/AllEventsWindow.cpp => module-apps/application-calendar/windows/AllEventsWindow.cpp +0 -105
@@ 1,105 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "AllEventsWindow.hpp"
#include "InputEvent.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include <gui/widgets/Window.hpp>
#include <header/AddElementAction.hpp>
#include <service-appmgr/Controller.hpp>

#include <module-db/queries/calendar/QueryEventsGetAllLimited.hpp>
#include <time/time_conversion.hpp>
#include <service-db/QueryMessage.hpp>
#include <service-db/DBNotificationMessage.hpp>

namespace gui
{

    AllEventsWindow::AllEventsWindow(app::Application *app, std::string name)
        : AppWindow(app, style::window::calendar::name::all_events_window),
          allEventsModel{std::make_shared<AllEventsModel>(this->application)}
    {
        buildInterface();
    }

    void AllEventsWindow::rebuild()
    {
        buildInterface();
    }

    void AllEventsWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::open));
        bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate("app_calendar_bar_month"));

        setTitle(utils::translate("app_calendar_title_main"));
        header->navigationIndicatorAdd(new gui::header::AddElementAction(), gui::header::BoxSelection::Left);

        allEventsList = new gui::CalendarListView(this,
                                                  style::window::calendar::listView_x,
                                                  style::window::calendar::listView_y,
                                                  style::window::calendar::listView_w,
                                                  style::window::calendar::listView_h,
                                                  allEventsModel);
        setFocusItem(allEventsList);
    }

    void AllEventsWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data)
    {
        allEventsList->rebuildList();
    }

    auto AllEventsWindow::handleSwitchData(SwitchData *data) -> bool
    {
        if (data == nullptr) {
            return false;
        }
        auto *item = dynamic_cast<DayMonthData *>(data);
        if (item == nullptr) {
            return false;
        }
        dateFilter = item->getDateFilter();
        return true;
    }

    bool AllEventsWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_LEFT)) {
            LOG_DEBUG("Switch to new event window");
            auto event       = std::make_shared<EventsRecord>();
            event->date_from = dateFilter;
            event->date_till = dateFilter + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                               std::chrono::minutes(utils::time::Locale::max_minutes);
            auto data = std::make_unique<EventRecordData>(std::move(event));
            data->setDescription(style::window::calendar::new_event);
            application->switchWindow(
                style::window::calendar::name::new_edit_event, gui::ShowMode::GUI_SHOW_INIT, std::move(data));
            return true;
        }
        return false;
    }

    bool AllEventsWindow::onDatabaseMessage(sys::Message *msgl)
    {
        auto *msgNotification = dynamic_cast<db::NotificationMessage *>(msgl);
        if (msgNotification != nullptr) {
            if (msgNotification->interface == db::Interface::Name::Events) {
                if (msgNotification->dataModified()) {
                    allEventsList->rebuildList(gui::listview::RebuildType::InPlace);
                    return true;
                }
            }
        }
        return false;
    }
} /* namespace gui */

D module-apps/application-calendar/windows/AllEventsWindow.hpp => module-apps/application-calendar/windows/AllEventsWindow.hpp +0 -31
@@ 1,31 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "application-calendar/models/AllEventsModel.hpp"
#include "application-calendar/widgets/CalendarListView.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "windows/AppWindow.hpp"
#include "Application.hpp"
#include <ListView.hpp>

namespace gui
{
    class AllEventsWindow : public gui::AppWindow
    {
        TimePoint dateFilter                           = TimePointNow();
        gui::ListView *allEventsList                   = nullptr;
        std::shared_ptr<AllEventsModel> allEventsModel = nullptr;

      public:
        AllEventsWindow(app::Application *app, std::string name);
        void onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) override;
        bool onDatabaseMessage(sys::Message *msgl) override;

        bool onInput(const gui::InputEvent &inputEvent) override;
        void rebuild() override;
        void buildInterface() override;
        auto handleSwitchData(SwitchData *data) -> bool override;
    };

} // namespace gui

D module-apps/application-calendar/windows/CalendarEventsOptionsWindow.cpp => module-apps/application-calendar/windows/CalendarEventsOptionsWindow.cpp +0 -76
@@ 1,76 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarEventsOptionsWindow.hpp"
#include "DialogMetadataMessage.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "Dialog.hpp"
#include <Utils.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <module-db/queries/calendar/QueryEventsRemove.hpp>
#include <module-apps/application-calendar/data/CalendarData.hpp>

namespace gui
{

    CalendarEventsOptions::CalendarEventsOptions(app::Application *app)
        : OptionWindow(app, style::window::calendar::name::events_options)
    {}

    auto CalendarEventsOptions::eventsOptionsList() -> std::list<gui::Option>
    {
        std::list<gui::Option> options;
        options.emplace_back(gui::Option{utils::translate("app_calendar_options_edit"), [=](gui::Item &item) {
                                             LOG_INFO("Switch to edit window");
                                             auto rec  = std::make_unique<EventsRecord>(*eventRecord);
                                             auto data = std::make_unique<EventRecordData>(std::move(rec));
                                             data->setDescription("Edit");
                                             application->switchWindow(style::window::calendar::name::new_edit_event,
                                                                       std::move(data));
                                             return true;
                                         }});
        options.emplace_back(gui::Option{utils::translate("app_calendar_options_delete"),
                                         [=](gui::Item &item) { return eventDelete(); }});
        return options;
    }

    auto CalendarEventsOptions::handleSwitchData(SwitchData *data) -> bool
    {
        if (data == nullptr) {
            return false;
        }

        auto *item = dynamic_cast<EventRecordData *>(data);
        if (item == nullptr) {
            return false;
        }

        eventRecord = item->getData();
        clearOptions();
        addOptions(eventsOptionsList());
        return true;
    }

    auto CalendarEventsOptions::eventDelete() -> bool
    {
        LOG_DEBUG("Switch to delete event window");

        auto metaData = std::make_unique<gui::DialogMetadataMessage>(gui::DialogMetadata{
            eventRecord->title,
            "phonebook_contact_delete_trashcan",
            utils::translate("app_calendar_event_delete_confirmation"),
            "",
            [=]() -> bool {
                LOG_INFO("Delete calendar event %d", static_cast<int>(eventRecord->ID));
                DBServiceAPI::GetQuery(application,
                                       db::Interface::Name::Events,
                                       std::make_unique<db::query::events::Remove>(eventRecord->ID));
                const uint32_t numberOfIgnoredWindows = 3;
                application->returnToPreviousWindow(numberOfIgnoredWindows);
                return true;
            }});
        this->application->switchWindow(
            style::window::calendar::name::dialog_yes_no, gui::ShowMode::GUI_SHOW_INIT, std::move(metaData));
        return true;
    }
} // namespace gui

D module-apps/application-calendar/windows/CalendarEventsOptionsWindow.hpp => module-apps/application-calendar/windows/CalendarEventsOptionsWindow.hpp +0 -25
@@ 1,25 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-db/Interface/EventsRecord.hpp>
#include "OptionWindow.hpp"

namespace gui
{
    class CalendarEventsOptions : public OptionWindow
    {
        std::shared_ptr<EventsRecord> eventRecord;
        std::string goBackWindowName;

      public:
        CalendarEventsOptions(app::Application *app);
        ~CalendarEventsOptions() override = default;
        auto handleSwitchData(SwitchData *data) -> bool override;

      private:
        auto eventsOptionsList() -> std::list<gui::Option>;
        auto eventDelete() -> bool;
    };
}; // namespace gui

M module-apps/application-calendar/windows/CalendarMainWindow.cpp => module-apps/application-calendar/windows/CalendarMainWindow.cpp +37 -112
@@ 5,14 5,8 @@
#include "application-calendar/ApplicationCalendar.hpp"
#include "application-calendar/models/MonthModel.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include "NoEvents.hpp"
#include <module-db/queries/calendar/QueryEventsGetAll.hpp>
#include <module-db/queries/calendar/QueryEventsGetFiltered.hpp>
#include <time/time_conversion.hpp>
#include <service-db/QueryMessage.hpp>
#include <service-db/DBServiceAPI.hpp>
#include <module-db/Common/Query.hpp>
#include <InputEvent.hpp>

namespace gui
{


@@ 20,21 14,17 @@ namespace gui
    CalendarMainWindow::CalendarMainWindow(app::Application *app, const std::string &name)
        : AppWindow(app, name), app::AsyncCallbackReceiver{app}
    {
        auto appCalendar = dynamic_cast<app::ApplicationCalendar *>(application);
        assert(appCalendar != nullptr);
        std::chrono::system_clock::time_point tp =
            std::chrono::system_clock::from_time_t(appCalendar->getCurrentTimeStamp());
        std::chrono::system_clock::time_point tp = std::chrono::system_clock::now();
        this->actualDate = date::year_month_day{date::floor<date::days>(tp)};
        std::fill(std::begin(isDayEmpty), std::end(isDayEmpty), true);
        buildInterface();
    }

    void CalendarMainWindow::refresh(const std::vector<EventsRecord> &records)
    void CalendarMainWindow::refresh()
    {
        erase(dateLabel);
        monthBox->erase();
        monthModel           = std::make_unique<MonthModel>(actualDate);
        monthModel->markEventsInDays(records, isDayEmpty);
        std::string dateText = monthModel->getMonthYearText();
        this->buildMonth(monthModel);
        this->buildDateLabel(dateText);


@@ 57,10 47,8 @@ namespace gui
        dayWidth      = style::window::calendar::day_cell_width;
        dayHeight     = style::window::calendar::day_cell_height;

        // create empty month box
        monthBox = new MonthBox(
            app, this, offsetFromTop, monthWidth, monthHeight, dayWidth, dayHeight, monthModel, isDayEmpty);
        // setup month box
        addWidget(monthBox);

        monthBox->borderCallback = [this](const InputEvent &inputEvent) -> bool {


@@ 69,53 57,31 @@ namespace gui
            }
            switch (inputEvent.getKeyCode()) {
            case KeyCode::KEY_UP: {
                LOG_DEBUG("change month prev");
                if (actualDate.month() != date::January) {
                    date::month prevMonth = --actualDate.month();
                    actualDate            = actualDate.year() / prevMonth / date::last;
                }
                else {
                    date::month prevMonth = date::December;
                    date::year prevYear   = --actualDate.year();
                    actualDate            = prevYear / prevMonth / date::last;
                }

                filterRequest();
                decrementMonthInView();
                return true;
            }
            case KeyCode::KEY_DOWN: {
                LOG_DEBUG("change month next");
                if (actualDate.month() != date::December) {
                    date::month nextMonth = ++actualDate.month();
                    actualDate            = actualDate.year() / nextMonth / 1;
                }
                else {
                    date::month nextMonth = date::January;
                    date::year nextYear   = ++actualDate.year();
                    actualDate            = nextYear / nextMonth / 1;
                }

                filterRequest();
                incrementMonthInView();
                return true;
            }
            case KeyCode::KEY_LEFT: {
                LOG_DEBUG("Call borderCallback -> go to the previous element");
                auto it = std::prev(monthBox->getNavigationFocusedItem());
                if (it != monthBox->children.end() && (*it)->isActive()) {
                    monthBox->setFocusItem(*it);
                }
                else {
                    decrementMonthInView();
                    monthBox->setFocusOnLastElement();
                }
                return true;
            }
            case KeyCode::KEY_RIGHT: {
                LOG_DEBUG("Call borderCallback -> go to the next element");
                auto it = std::next(monthBox->getNavigationFocusedItem());
                if (it != monthBox->children.end() && (*it)->isActive()) {
                    monthBox->setFocusItem(*it);
                }
                else {
                    incrementMonthInView();
                    monthBox->setFocusOnElement(0);
                }
                return true;


@@ 129,6 95,36 @@ namespace gui
        setFocusItem(monthBox);
    }

    void CalendarMainWindow::decrementMonthInView()
    {
        if (actualDate.month() != date::January) {
            date::month prevMonth = --actualDate.month();
            actualDate            = actualDate.year() / prevMonth / date::last;
        }
        else {
            date::month prevMonth = date::December;
            date::year prevYear   = --actualDate.year();
            actualDate            = prevYear / prevMonth / date::last;
        }
        refresh();
        monthBox->setFocusOnLastElement();
    }

    void CalendarMainWindow::incrementMonthInView()
    {
        if (actualDate.month() != date::December) {
            date::month nextMonth = ++actualDate.month();
            actualDate            = actualDate.year() / nextMonth / 1;
        }
        else {
            date::month nextMonth = date::January;
            date::year nextYear   = ++actualDate.year();
            actualDate            = nextYear / nextMonth / 1;
        }
        refresh();
        monthBox->setFocusOnElement(0);
    }

    void CalendarMainWindow::buildDateLabel(std::string actualDateTime)
    {
        dateLabel = new Label(this,


@@ 146,24 142,16 @@ namespace gui

    void CalendarMainWindow::buildInterface()
    {
        LOG_DEBUG("AppWindow build interface");
        AppWindow::buildInterface();

        LOG_DEBUG("Start build interface for calendar main window");

        setTitle(utils::translate("app_calendar_title_main"));

        monthModel = std::make_unique<MonthModel>(actualDate);
        filterRequest();
        this->buildMonth(monthModel);
        this->buildDateLabel(monthModel->getMonthYearText());

        bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setActive(gui::BottomBar::Side::LEFT, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::open));
        bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate("app_calendar_bar_list"));
    }

    void CalendarMainWindow::destroyInterface()


@@ 176,70 164,7 @@ namespace gui
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_LF)) {
            auto app = dynamic_cast<app::ApplicationCalendar *>(application);
            assert(application != nullptr);
            app->setEquivalentToEmptyWindow(EquivalentWindow::AllEventsWindow);
            auto query = std::make_unique<db::query::events::GetAll>();
            auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
            task->setCallback([this](auto response) { return handleQueryResponse(response); });
            task->execute(application, this);
            return true;
        }

        return false;
    }

    void CalendarMainWindow::filterRequest()
    {
        calendar::YearMonthDay date_from = actualDate.year() / actualDate.month() / 1;
        calendar::YearMonthDay date_till = date_from + date::months{1};
        auto filter_from       = TimePointFromYearMonthDay(date_from);
        auto filter_till       = TimePointFromYearMonthDay(date_till);
        LOG_DEBUG("filter:  %s", TimePointToString(filter_till).c_str());
        auto query = std::make_unique<db::query::events::GetFiltered>(filter_from, filter_till);
        auto task  = app::AsyncQuery::createFromQuery(std::move(query), db::Interface::Name::Events);
        task->setCallback([this](auto response) { return handleQueryResponse(response); });
        task->execute(application, this);
    }

    auto CalendarMainWindow::handleQueryResponse(db::QueryResult *queryResult) -> bool
    {
        std::fill(std::begin(isDayEmpty), std::end(isDayEmpty), true);
        if (auto response = dynamic_cast<db::query::events::GetFilteredResult *>(queryResult)) {
            const auto records = response->getResult();
            refresh(records);
            return true;
        }
        if (auto response = dynamic_cast<db::query::events::GetAllResult *>(queryResult)) {
            const auto records = response->getResult();
            auto day           = monthBox->getFocusItemIndex() + 1;
            auto filter = TimePointFromYearMonthDay(monthModel->getYear() / monthModel->getMonth() / date::day(day));
            if (!records.empty()) {
                auto data = std::make_unique<DayMonthData>();
                data->setData("", filter);
                application->switchWindow(style::window::calendar::name::all_events_window, std::move(data));
            }
            else {
                auto appCalendar = dynamic_cast<app::ApplicationCalendar *>(application);
                assert(appCalendar != nullptr);
                appCalendar->switchToNoEventsWindow(utils::translate("app_calendar_title_main"), filter);
            }
            return true;
        }
        LOG_DEBUG("Response False");
        return false;
    }

    void CalendarMainWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (mode == ShowMode::GUI_SHOW_RETURN) {
            returnedFromWindow = true;
            auto day           = dynamic_cast<DayLabel *>(monthBox->getFocusItem());
            dayFocusedBefore   = day->getDayNumber();
        }
        filterRequest();
    }

} // namespace gui

M module-apps/application-calendar/windows/CalendarMainWindow.hpp => module-apps/application-calendar/windows/CalendarMainWindow.hpp +9 -17
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 21,15 21,17 @@ namespace db

namespace gui
{

    class CalendarMainWindow : public gui::AppWindow, public app::AsyncCallbackReceiver
    {
        std::array<bool, 31> isDayEmpty;
        uint32_t offsetFromTop = 0;
        uint32_t monthWidth    = 0;
        uint32_t monthHeight   = 0;
        uint32_t dayWidth      = 0;
        uint32_t dayHeight     = 0;
        std::uint32_t offsetFromTop = 0;
        std::uint32_t monthWidth    = 0;
        std::uint32_t monthHeight   = 0;
        std::uint32_t dayWidth      = 0;
        std::uint32_t dayHeight     = 0;
        void refresh();
        void decrementMonthInView();
        void incrementMonthInView();

      protected:
        date::year_month_day actualDate;


@@ 41,21 43,11 @@ namespace gui
        CalendarMainWindow(app::Application *app, const std::string &name);

        void rebuild() override;
        void refresh(const std::vector<EventsRecord> &records);
        void filterRequest();
        void buildMonth(std::unique_ptr<MonthModel> &model);
        void buildDateLabel(std::string actualDateTime);
        void buildInterface() override;
        void destroyInterface() override;
        bool onInput(const gui::InputEvent &inputEvent) override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        auto handleQueryResponse(db::QueryResult *queryResult) -> bool;
        std::unique_ptr<MonthModel> getMonthModel()
        {
            return std::move(monthModel);
        }
        bool returnedFromWindow   = false;
        uint32_t dayFocusedBefore = 0;
    };

} // namespace gui

D module-apps/application-calendar/windows/CustomRepeatWindow.cpp => module-apps/application-calendar/windows/CustomRepeatWindow.cpp +0 -87
@@ 1,87 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CustomRepeatWindow.hpp"
#include "InputEvent.hpp"
#include "application-calendar/widgets/CheckBoxWithLabelItem.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include <Utils.hpp>
#include <gui/widgets/Window.hpp>

namespace gui
{

    CustomRepeatWindow::CustomRepeatWindow(app::Application *app, std::string name)
        : AppWindow(app, style::window::calendar::name::custom_repeat_window),
          customRepeatModel{std::make_shared<CustomRepeatModel>(this->application)}
    {
        buildInterface();
    }

    void CustomRepeatWindow::rebuild()
    {
        erase();
        buildInterface();
    }

    void CustomRepeatWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::save));

        setTitle(utils::translate("app_calendar_custom_repeat_title"));
        list = new gui::ListView(this,
                                 style::window::calendar::listView_x,
                                 style::window::calendar::listView_y,
                                 style::window::calendar::listView_w,
                                 style::window::calendar::listView_h,
                                 customRepeatModel,
                                 gui::listview::ScrollBarType::None);
        setFocusItem(list);
    }

    void CustomRepeatWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (mode == ShowMode::GUI_SHOW_INIT) {
            list->rebuildList();
        }

        auto recievedData = dynamic_cast<WeekDaysRepeatData *>(data);
        if (recievedData != nullptr) {
            weekDaysOptData = std::make_shared<WeekDaysRepeatData>(*recievedData);
        }
        else {
            weekDaysOptData = std::make_shared<WeekDaysRepeatData>();
        }
        customRepeatModel->loadData(weekDaysOptData);
    }

    bool CustomRepeatWindow::onInput(const InputEvent &inputEvent)
    {
        // check if any of the lower inheritance onInput methods catch the event
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }
        // process only if key is released
        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            if (weekDaysOptData != nullptr) {
                auto isCheckedData = customRepeatModel->getIsCheckedData();
                uint32_t i         = 0;
                for (auto checked : isCheckedData) {
                    weekDaysOptData->setData(i, checked);
                    ++i;
                }
                auto data = weekDaysOptData.get();
                application->switchWindow(style::window::calendar::name::new_edit_event,
                                          gui::ShowMode::GUI_SHOW_RETURN,
                                          std::make_unique<WeekDaysRepeatData>(*data));
                return true;
            }
        }
        return false;
    }
} /* namespace gui */

D module-apps/application-calendar/windows/CustomRepeatWindow.hpp => module-apps/application-calendar/windows/CustomRepeatWindow.hpp +0 -32
@@ 1,32 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/models/CustomRepeatModel.hpp"
#include "windows/AppWindow.hpp"
#include "Application.hpp"
#include <ListView.hpp>

namespace gui
{
    class CustomRepeatWindow : public gui::AppWindow
    {
        gui::ListView *list                                  = nullptr;
        std::shared_ptr<CustomRepeatModel> customRepeatModel = nullptr;
        std::shared_ptr<WeekDaysRepeatData> weekDaysOptData  = nullptr;

      public:
        CustomRepeatWindow(app::Application *app, std::string name);

        void rebuild() override;
        void buildInterface() override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool onInput(const InputEvent &inputEvent) override;
        void setWeekDaysOptionData(std::unique_ptr<WeekDaysRepeatData> data)
        {
            weekDaysOptData = std::move(data);
        }
    };

} /* namespace gui */

D module-apps/application-calendar/windows/DayEventsWindow.cpp => module-apps/application-calendar/windows/DayEventsWindow.cpp +0 -118
@@ 1,118 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "DayEventsWindow.hpp"
#include "application-calendar/data/CalendarData.hpp"
#include "application-calendar/ApplicationCalendar.hpp"
#include <gui/widgets/Window.hpp>
#include <gui/widgets/Label.hpp>
#include <gui/widgets/Item.hpp>
#include <gui/widgets/Image.hpp>
#include <header/AddElementAction.hpp>

#include <time/time_conversion.hpp>
#include <module-db/queries/calendar/QueryEventsGetFiltered.hpp>
#include <service-db/QueryMessage.hpp>
#include <service-db/DBNotificationMessage.hpp>

namespace gui
{

    DayEventsWindow::DayEventsWindow(app::Application *app)
        : AppWindow(app, style::window::calendar::name::day_events_window),
          dayEventsModel{std::make_shared<DayEventsModel>(this->application)}
    {
        buildInterface();
    }

    void DayEventsWindow::rebuild()
    {
        buildInterface();
    }
    void DayEventsWindow::onBeforeShow(ShowMode mode, SwitchData *data)
    {
        if (mode == gui::ShowMode::GUI_SHOW_INIT) {
            dayEventsList->rebuildList();
        }
    }

    auto DayEventsWindow::handleSwitchData(SwitchData *data) -> bool
    {
        if (data == nullptr) {
            return false;
        }

        auto *item = dynamic_cast<DayMonthData *>(data);
        if (item == nullptr) {
            return false;
        }

        dayMonthTitle   = item->getDayMonthText();
        filterFrom      = item->getDateFilter();
        auto filterTill = filterFrom + date::days{1};
        dayEventsModel->setFilters(filterFrom, filterTill, dayMonthTitle);
        LOG_DEBUG("FILTER 1: %s", TimePointToString(filterFrom).c_str());
        setTitle(dayMonthTitle);
        if (dayMonthTitle == "") {
            return false;
        }

        return true;
    }

    void DayEventsWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::open));

        setTitle(dayMonthTitle);
        header->navigationIndicatorAdd(new gui::header::AddElementAction(), gui::header::BoxSelection::Left);

        dayEventsList = new gui::ListView(this,
                                          style::window::calendar::listView_x,
                                          style::window::calendar::listView_y,
                                          style::window::calendar::listView_w,
                                          style::window::calendar::listView_h,
                                          dayEventsModel,
                                          gui::listview::ScrollBarType::Fixed);
        setFocusItem(dayEventsList);
    }

    bool DayEventsWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_LEFT)) {
            LOG_DEBUG("Switch to new window - edit window");
            auto event       = std::make_shared<EventsRecord>();
            event->date_from = filterFrom;
            event->date_till = filterFrom + std::chrono::hours(utils::time::Locale::max_hour_24H_mode) +
                               std::chrono::minutes(utils::time::Locale::max_minutes);
            auto data = std::make_unique<EventRecordData>(std::move(event));
            data->setDescription(style::window::calendar::new_event);
            application->switchWindow(
                style::window::calendar::name::new_edit_event, gui::ShowMode::GUI_SHOW_INIT, std::move(data));
            return true;
        }
        return false;
    }

    bool DayEventsWindow::onDatabaseMessage(sys::Message *msgl)
    {
        auto *msgNotification = dynamic_cast<db::NotificationMessage *>(msgl);
        if (msgNotification != nullptr) {
            if (msgNotification->interface == db::Interface::Name::Events) {
                if (msgNotification->dataModified()) {
                    dayEventsList->rebuildList(gui::listview::RebuildType::InPlace);
                    return true;
                }
            }
        }
        return false;
    }
} /* namespace gui */

D module-apps/application-calendar/windows/DayEventsWindow.hpp => module-apps/application-calendar/windows/DayEventsWindow.hpp +0 -38
@@ 1,38 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-calendar/models/DayEventsModel.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "windows/AppWindow.hpp"
#include "Application.hpp"
#include "NoEvents.hpp"

#include <ListView.hpp>
#include <gui/widgets/Item.hpp>
#include <gui/widgets/Label.hpp>
#include <application-calendar/data/dateCommon.hpp>

namespace gui
{
    class Image;

    class DayEventsWindow : public gui::AppWindow
    {
        std::string dayMonthTitle;
        TimePoint filterFrom;
        gui::ListView *dayEventsList                             = nullptr;
        std::shared_ptr<DayEventsModel> dayEventsModel           = nullptr;

      public:
        DayEventsWindow(app::Application *app);
        bool handleSwitchData(SwitchData *data) override;
        bool onInput(const gui::InputEvent &inputEvent) override;
        bool onDatabaseMessage(sys::Message *msgl) override;
        void rebuild() override;
        void buildInterface() override;
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
    };

} /* namespace app */

D module-apps/application-calendar/windows/EventDetailWindow.cpp => module-apps/application-calendar/windows/EventDetailWindow.cpp +0 -92
@@ 1,92 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventDetailWindow.hpp"
#include "InputEvent.hpp"
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include <gui/widgets/Window.hpp>
#include <time/time_conversion.hpp>

namespace gui
{

    EventDetailWindow::EventDetailWindow(app::Application *app, std::string name)
        : AppWindow(app, style::window::calendar::name::details_window), eventDetailModel{
                                                                             std::make_shared<EventDetailModel>(app)}
    {
        buildInterface();
    }

    void EventDetailWindow::rebuild()
    {
        erase();
        buildInterface();
    }

    void EventDetailWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setActive(gui::BottomBar::Side::LEFT, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate(style::strings::common::options));

        bodyList = new gui::ListView(this,
                                     style::window::calendar::listView_x,
                                     style::window::calendar::listView_y,
                                     style::window::calendar::listView_w,
                                     style::window::calendar::listView_h,
                                     eventDetailModel,
                                     gui::listview::ScrollBarType::PreRendered);

        setFocusItem(bodyList);
    }

    void EventDetailWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data)
    {
        if (mode == gui::ShowMode::GUI_SHOW_INIT) {
            bodyList->rebuildList();
        }

        eventDetailModel->loadData(eventRecord);
    }

    auto EventDetailWindow::handleSwitchData(SwitchData *data) -> bool
    {
        if (data == nullptr) {
            return false;
        }

        auto *item = dynamic_cast<EventRecordData *>(data);
        if (item == nullptr) {
            return false;
        }

        eventRecord    = item->getData();
        auto startDate = TimePointToYearMonthDay(eventRecord->date_from);
        std::string monthStr =
            utils::time::Locale::get_month(utils::time::Locale::Month(unsigned(startDate.month()) - 1));
        setTitle(std::to_string(unsigned(startDate.day())) + " " + monthStr);

        return true;
    }

    bool EventDetailWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_LF)) {
            LOG_DEBUG("Switch to option window");
            auto rec  = std::make_unique<EventsRecord>(*eventRecord);
            auto data = std::make_unique<EventRecordData>(std::move(rec));
            application->switchWindow(style::window::calendar::name::events_options, std::move(data));
            return true;
        }

        return false;
    }
} /* namespace gui */

D module-apps/application-calendar/windows/EventDetailWindow.hpp => module-apps/application-calendar/windows/EventDetailWindow.hpp +0 -31
@@ 1,31 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "application-calendar/models/EventDetailModel.hpp"
#include "windows/AppWindow.hpp"
#include "Application.hpp"
#include <Style.hpp>
#include <ListView.hpp>
#include <module-db/Interface/EventsRecord.hpp>

namespace gui
{
    class EventDetailWindow : public gui::AppWindow
    {
        std::shared_ptr<EventsRecord> eventRecord          = nullptr;
        std::shared_ptr<EventDetailModel> eventDetailModel = nullptr;
        gui::ListView *bodyList                            = nullptr;

      public:
        EventDetailWindow(app::Application *app, std::string name);
        virtual ~EventDetailWindow() override = default;

        bool onInput(const gui::InputEvent &inputEvent) override;
        void onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) override;
        void rebuild() override;
        void buildInterface() override;
        auto handleSwitchData(SwitchData *data) -> bool override;
    };

} /* namespace gui */

D module-apps/application-calendar/windows/EventReminderWindow.cpp => module-apps/application-calendar/windows/EventReminderWindow.cpp +0 -163
@@ 1,163 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventReminderWindow.hpp"
#include <Timers/TimerFactory.hpp>
#include "application-calendar/widgets/CalendarStyle.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include <gui/widgets/Window.hpp>
#include <time/time_conversion.hpp>
#include "service-appmgr/Controller.hpp"
#include <Image.hpp>

namespace gui
{
    constexpr static const int reminderLifeDuration = 20000;

    EventReminderWindow::EventReminderWindow(app::Application *app, std::string name)
        : AppWindow(app, style::window::calendar::name::event_reminder_window)
    {
        buildInterface();

        reminderTimer = sys::TimerFactory::createSingleShotTimer(app,
                                                                 "CalendarReminderTimer",
                                                                 std::chrono::milliseconds{reminderLifeDuration},
                                                                 [this](sys::Timer &) { reminderTimerCallback(); });
    }

    EventReminderWindow::~EventReminderWindow()
    {
        destroyTimer();
    }

    void EventReminderWindow::onClose()
    {
        destroyTimer();
    }

    void EventReminderWindow::rebuild()
    {
        erase();
        buildInterface();
    }

    status_bar::Configuration EventReminderWindow::configureStatusBar(status_bar::Configuration appConfiguration)
    {
        using namespace status_bar;
        appConfiguration.enable({Indicator::Signal, Indicator::Time, Indicator::Battery, Indicator::SimCard});
        return appConfiguration;
    }

    void EventReminderWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::ok));
        bottomBar->setBorderColor(ColorNoColor);

        const uint32_t w = this->getWidth() - style::window::default_left_margin - style::window::default_right_margin;
        const uint32_t h = this->getHeight() - bottomBar->getHeight();
        body             = new gui::VBox(this, style::window::default_left_margin, statusBar->getHeight(), w, h);
        body->setBorderColor(gui::ColorNoColor);

        topImage = new gui::Image(body,
                                  style::window::calendar::imageCircleTop::x,
                                  style::window::calendar::imageCircleTop::y,
                                  0,
                                  0,
                                  style::window::calendar::imageCircleTop::name);
        topImage->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center));

        dateLabel = new Label(body, 0, 0, w, style::window::label::default_h);
        dateLabel->setFont(style::window::font::smallbold);
        dateLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        dateLabel->setBorderColor(ColorNoColor);

        timeLabel = new Label(body, 0, 0, w, style::window::label::default_h);
        timeLabel->setFont(style::window::font::largelight);
        timeLabel->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        timeLabel->setBorderColor(ColorNoColor);

        bottomImage = new gui::Image(body,
                                     style::window::calendar::imageCircleBottom::x,
                                     style::window::calendar::imageCircleBottom::y,
                                     0,
                                     0,
                                     style::window::calendar::imageCircleBottom::name);
        bottomImage->setAlignment(gui::Alignment(gui::Alignment::Horizontal::Center));

        descriptionLabel = new Label(body, 0, 0, w, 2 * style::window::label::default_h);
        descriptionLabel->setFont(style::window::font::big);
        descriptionLabel->setAlignment(
            gui::Alignment(gui::Alignment::Horizontal::Center, gui::Alignment::Vertical::Center));
        descriptionLabel->setBorderColor(ColorNoColor);

        setFocusItem(body);
    }

    void EventReminderWindow::destroyInterface()
    {
        destroyTimer();
        erase();
    }

    void EventReminderWindow::startTimer()
    {
        reminderTimer.start();
    }

    void EventReminderWindow::destroyTimer()
    {
        reminderTimer.stop();
    }

    auto EventReminderWindow::handleSwitchData(SwitchData *data) -> bool
    {
        if (data == nullptr) {
            return false;
        }

        auto *item = dynamic_cast<EventRecordData *>(data);
        if (item == nullptr) {
            return false;
        }

        eventRecord = item->getData();
        dateLabel->setText(TimePointToLocalizedDateString(eventRecord->date_from));
        timeLabel->setText(TimePointToLocalizedTimeString(eventRecord->date_from));
        descriptionLabel->setText(eventRecord->title);

        startTimer();

        return true;
    }

    bool EventReminderWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            closeReminder();
            return true;
        }

        return false;
    }

    void EventReminderWindow::reminderTimerCallback()
    {
        closeReminder();
    }

    void EventReminderWindow::closeReminder()
    {
        LOG_DEBUG("Switch to previous window");
        destroyTimer();

        app::manager::Controller::sendAction(application, app::manager::actions::Home);
    }

} /* namespace gui */

D module-apps/application-calendar/windows/EventReminderWindow.hpp => module-apps/application-calendar/windows/EventReminderWindow.hpp +0 -51
@@ 1,51 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "application-calendar/models/EventDetailModel.hpp"
#include "windows/AppWindow.hpp"
#include "Application.hpp"
#include <Style.hpp>
#include <module-db/Interface/EventsRecord.hpp>
#include <Text.hpp>
#include <Label.hpp>
#include <module-sys/Timers/TimerHandle.hpp>

namespace gui
{
    class Image;

    class EventReminderWindow : public gui::AppWindow
    {
      private:
        sys::TimerHandle reminderTimer;

      protected:
        std::shared_ptr<EventsRecord> eventRecord;

        gui::VBox *body         = nullptr;
        Label *dateLabel        = nullptr;
        Label *timeLabel        = nullptr;
        Label *descriptionLabel = nullptr;
        Image *topImage         = nullptr;
        Image *bottomImage      = nullptr;

        void startTimer();
        void destroyTimer();
        void reminderTimerCallback();
        void closeReminder();

      public:
        EventReminderWindow(app::Application *app, std::string name);
        virtual ~EventReminderWindow();

        void onClose() override;
        bool onInput(const gui::InputEvent &inputEvent) override;
        void rebuild() override;
        void buildInterface() override;
        void destroyInterface() override;
        status_bar::Configuration configureStatusBar(status_bar::Configuration appConfiguration) override;
        auto handleSwitchData(SwitchData *data) -> bool override;
    };

} /* namespace gui */

D module-apps/application-calendar/windows/NewEditEventWindow.cpp => module-apps/application-calendar/windows/NewEditEventWindow.cpp +0 -102
@@ 1,102 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NewEditEventWindow.hpp"
#include <module-db/Interface/EventsRecord.hpp>
#include <service-db/DBServiceAPI.hpp>

namespace gui
{

    NewEditEventWindow::NewEditEventWindow(app::Application *app, std::string name)
        : AppWindow(app, style::window::calendar::name::new_edit_event),
          newEditEventModel{std::make_shared<NewEditEventModel>(this->application)}
    {
        buildInterface();
    }

    void NewEditEventWindow::rebuild()
    {
        erase();
        buildInterface();
    }

    void NewEditEventWindow::buildInterface()
    {
        AppWindow::buildInterface();

        bottomBar->setActive(gui::BottomBar::Side::RIGHT, true);
        bottomBar->setActive(gui::BottomBar::Side::CENTER, true);
        bottomBar->setText(gui::BottomBar::Side::RIGHT, utils::translate(style::strings::common::back));
        bottomBar->setText(gui::BottomBar::Side::CENTER, utils::translate(style::strings::common::save));

        list = new gui::ListView(this,
                                 style::window::calendar::listView_x,
                                 style::window::calendar::listView_y,
                                 style::window::calendar::listView_w,
                                 style::window::calendar::listView_h,
                                 newEditEventModel,
                                 gui::listview::ScrollBarType::PreRendered);
        setFocusItem(list);
    }

    void NewEditEventWindow::onBeforeShow(gui::ShowMode mode, gui::SwitchData *data)
    {
        switch (eventAction) {
        case EventAction::Add: {
            setTitle(utils::translate("app_calendar_new_event_title"));
            break;
        }
        case EventAction::Edit:
            setTitle(utils::translate("app_calendar_edit_event_title"));
            break;
        }

        if (mode == ShowMode::GUI_SHOW_INIT) {
            auto rec = dynamic_cast<EventRecordData *>(data);
            if (rec != nullptr) {
                eventRecord = rec->getData();
            }
            newEditEventModel->loadData(eventRecord);
        }
        if (mode == ShowMode::GUI_SHOW_RETURN) {
            auto receivedData = dynamic_cast<WeekDaysRepeatData *>(data);
            if (receivedData != nullptr) {
                auto parser         = std::make_unique<OptionParser>();
                auto uniqueData     = std::make_unique<WeekDaysRepeatData>(*receivedData);
                eventRecord->repeat = parser->getDatabaseFieldValue(std::move(uniqueData));
                newEditEventModel->loadRepeat(eventRecord);
            }
        }
    }

    bool NewEditEventWindow::onInput(const gui::InputEvent &inputEvent)
    {
        if (AppWindow::onInput(inputEvent)) {
            return true;
        }

        if (inputEvent.isShortRelease(gui::KeyCode::KEY_ENTER)) {
            LOG_DEBUG("Save Event");
            newEditEventModel->saveData(eventRecord, eventAction);
            return true;
        }

        return false;
    }

    bool NewEditEventWindow::handleSwitchData(gui::SwitchData *data)
    {
        if (data == nullptr) {
            return false;
        }

        if (data->getDescription() == style::window::calendar::edit_event) {
            eventAction = EventAction::Edit;
        }
        else if (data->getDescription() == style::window::calendar::new_event) {
            eventAction = EventAction::Add;
        }
        return true;
    }
} /* namespace gui */

D module-apps/application-calendar/windows/NewEditEventWindow.hpp => module-apps/application-calendar/windows/NewEditEventWindow.hpp +0 -39
@@ 1,39 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "application-calendar/widgets/CalendarStyle.hpp"
#include "application-calendar/models/NewEditEventModel.hpp"
#include "module-apps/application-calendar/data/CalendarData.hpp"
#include "module-apps/application-calendar/data/OptionParser.hpp"
#include "windows/AppWindow.hpp"
#include "Application.hpp"
#include <gui/widgets/Item.hpp>
#include <gui/widgets/Label.hpp>
#include <gui/widgets/Text.hpp>
#include <gui/widgets/ListItem.hpp>
#include <ListView.hpp>
#include <Utils.hpp>

namespace gui
{
    class NewEditEventWindow : public gui::AppWindow
    {
      private:
        std::shared_ptr<EventsRecord> eventRecord            = nullptr;
        gui::ListView *list                                  = nullptr;
        std::shared_ptr<NewEditEventModel> newEditEventModel = nullptr;
        EventAction eventAction                              = EventAction::Add;

      public:
        NewEditEventWindow(app::Application *app, std::string name);

        bool handleSwitchData(gui::SwitchData *data) override;
        void onBeforeShow(gui::ShowMode mode, gui::SwitchData *data) override;
        bool onInput(const gui::InputEvent &inputEvent) override;
        void rebuild() override;
        void buildInterface() override;
    };

} /* namespace gui */

M module-apps/application-desktop/windows/DesktopMainWindow.cpp => module-apps/application-desktop/windows/DesktopMainWindow.cpp +0 -2
@@ 115,8 115,6 @@ namespace gui
            application->switchWindow(app::window::name::desktop_post_update_window, std::move(data));
            getAppDesktop()->setOsUpdateVersion(updateos::initSysVer);
        }

        application->bus.sendUnicast(std::make_shared<TimersProcessingStartMessage>(), service::name::service_time);
    }

    void DesktopMainWindow::onBeforeShow(ShowMode mode, SwitchData *data)

M module-apps/application-settings-new/ApplicationSettings.cpp => module-apps/application-settings-new/ApplicationSettings.cpp +0 -6
@@ 20,7 20,6 @@
#include <application-settings-new/windows/NetworkWindow.hpp>
#include <application-settings-new/windows/PhoneWindow.hpp>
#include <application-settings-new/windows/MessagesWindow.hpp>
#include <application-settings-new/windows/CalendarWindow.hpp>
#include <application-settings-new/windows/AlarmClockWindow.hpp>
#include <application-settings-new/windows/SoundSelectWindow.hpp>
#include <application-settings-new/windows/PhoneNameWindow.hpp>


@@ 389,11 388,6 @@ namespace app
                app, audio_settings::PlaybackType::TextMessageRingtone);
            return std::make_unique<gui::MessagesWindow>(app, std::move(audioModel));
        });
        windowsFactory.attach(gui::window::name::calendar, [](Application *app, const std::string &name) {
            auto audioModel =
                std::make_unique<audio_settings::AudioSettingsModel>(app, audio_settings::PlaybackType::Notifications);
            return std::make_unique<gui::CalendarWindow>(app, std::move(audioModel));
        });
        windowsFactory.attach(gui::window::name::alarm_clock, [](Application *app, const std::string &name) {
            auto audioModel =
                std::make_unique<audio_settings::AudioSettingsModel>(app, audio_settings::PlaybackType::Alarm);

M module-apps/application-settings-new/ApplicationSettings.hpp => module-apps/application-settings-new/ApplicationSettings.hpp +0 -1
@@ 34,7 34,6 @@ namespace gui::window::name
    inline constexpr auto phone             = "Phone";
    inline constexpr auto messages          = "Messages";
    inline constexpr auto message_templates = "MessageTemplates";
    inline constexpr auto calendar          = "Calendar";
    inline constexpr auto alarm_clock       = "AlarmClock";
    inline constexpr auto sound_select      = "SoundSelect";
    inline constexpr auto torch             = "Torch";

M module-apps/application-settings-new/CMakeLists.txt => module-apps/application-settings-new/CMakeLists.txt +0 -1
@@ 53,7 53,6 @@ target_sources( ${PROJECT_NAME}
        windows/OptionsWidgetMaker.cpp
        windows/PhoneWindow.cpp
        windows/MessagesWindow.cpp
        windows/CalendarWindow.cpp
        windows/AlarmClockWindow.cpp
        windows/SoundSelectWindow.cpp
        windows/PhoneNameWindow.cpp

M module-apps/application-settings-new/windows/AppsAndToolsWindow.cpp => module-apps/application-settings-new/windows/AppsAndToolsWindow.cpp +0 -1
@@ 35,7 35,6 @@ namespace gui

        addMenu(i18("app_settings_apps_phone"), gui::window::name::phone);
        addMenu(i18("app_settings_apps_messages"), gui::window::name::messages);
        addMenu(i18("app_settings_apps_calendar"), gui::window::name::calendar);
        addMenu(i18("app_settings_apps_alarm_clock"), gui::window::name::alarm_clock);

        return optionList;

D module-apps/application-settings-new/windows/CalendarWindow.cpp => module-apps/application-settings-new/windows/CalendarWindow.cpp +0 -66
@@ 1,66 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarWindow.hpp"

#include <application-settings-new/data/SoundSelectData.hpp>
#include <application-settings-new/ApplicationSettings.hpp>
#include <i18n/i18n.hpp>
#include "BaseSettingsWindow.hpp"
#include <OptionSetting.hpp>

namespace gui
{
    CalendarWindow::CalendarWindow(app::Application *app,
                                   std::unique_ptr<audio_settings::AbstractAudioSettingsModel> &&audioModel)
        : BaseSettingsWindow(app, gui::window::name::calendar), mWidgetMaker(this), mAudioModel(std::move(audioModel))
    {
        setTitle(utils::translate("app_settings_apps_calendar"));
    }

    std::list<Option> CalendarWindow::buildOptionsList()
    {
        std::list<gui::Option> optionList;
        mVibrationsEnabled = mAudioModel->isVibrationEnabled();
        mSoundEnabled      = mAudioModel->isSoundEnabled();

        mWidgetMaker.addSwitchOption(optionList, utils::translate("app_settings_vibration"), mVibrationsEnabled, [&]() {
            switchVibrationState();
        });

        mWidgetMaker.addSwitchOption(
            optionList, utils::translate("app_settings_sound"), mSoundEnabled, [&]() { switchSoundState(); });

        if (mSoundEnabled) {
            mWidgetMaker.addSelectOption(
                optionList,
                utils::translate("app_settings_notification_sound"),
                [&]() { openNoticicationSoundWindow(); },
                true);
        }

        return optionList;
    }

    void CalendarWindow::switchVibrationState()
    {
        (mVibrationsEnabled) ? mAudioModel->setVibrationDisabled() : mAudioModel->setVibrationEnabled();
        refreshOptionsList();
    }

    void CalendarWindow::switchSoundState()
    {
        mSoundEnabled ? mAudioModel->setSoundDisabled() : mAudioModel->setSoundEnabled();
        refreshOptionsList();
    }

    void CalendarWindow::openNoticicationSoundWindow()
    {
        SoundSelectData::Info info;
        info.windowTitle = utils::translate("app_settings_notification_sound");
        info.audioModel  = mAudioModel.get();

        application->switchWindow(gui::window::name::sound_select, std::make_unique<SoundSelectData>(info));
    }

} // namespace gui

D module-apps/application-settings-new/windows/CalendarWindow.hpp => module-apps/application-settings-new/windows/CalendarWindow.hpp +0 -49
@@ 1,49 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "BaseSettingsWindow.hpp"
#include "OptionsWidgetMaker.hpp"
#include <application-settings-new/models/AudioSettingsModel.hpp>

namespace gui
{
    ///  @brief Calendar vibration and sound settings window
    ///
    class CalendarWindow : public BaseSettingsWindow
    {
      public:
        ///  Constructor
        ///  @app parent application pointer
        ///  @audioModel audio settings model
        explicit CalendarWindow(app::Application *app,
                                std::unique_ptr<audio_settings::AbstractAudioSettingsModel> &&audioModel);

      private:
        ///  Switches calendar vibration state
        void switchVibrationState();

        ///  Switches calendar sound state
        void switchSoundState();

        ///  Opens notification sound selection window
        void openNoticicationSoundWindow();

        ///  Build the list of options to be drawn
        ///  @return list of options
        std::list<Option> buildOptionsList() override;

        ///  Widget maker object
        OptionsWidgetMaker mWidgetMaker;

        ///  Flag describing if calendar vibration is enabled
        bool mVibrationsEnabled = false;

        ///  Flag describing if calendar sound is enabled
        bool mSoundEnabled = true;

        /// Audio settings model
        std::unique_ptr<audio_settings::AbstractAudioSettingsModel> mAudioModel;
    };
} // namespace gui

M module-apps/apps-common/CMakeLists.txt => module-apps/apps-common/CMakeLists.txt +0 -1
@@ 40,7 40,6 @@ target_sources(apps-common
        windows/AppWindow.cpp
        windows/BrightnessWindow.cpp
        windows/Dialog.cpp
        windows/NoEvents.cpp
        windows/OptionWindow.cpp
)


M module-apps/apps-common/popups/lock-popups/PhoneLockedWindow.cpp => module-apps/apps-common/popups/lock-popups/PhoneLockedWindow.cpp +1 -2
@@ 6,6 6,7 @@

#include <application-desktop/data/DesktopStyle.hpp>
#include <module-services/service-appmgr/service-appmgr/Controller.hpp>
#include <time/time_conversion_factory.hpp>
#include <service-time/ServiceTime.hpp>
#include <service-time/TimeMessage.hpp>



@@ 71,8 72,6 @@ namespace gui
        bottomBar->setActive(BottomBar::Side::RIGHT, false);
        bottomBar->setText(BottomBar::Side::CENTER, utils::translate("app_desktop_unlock"));
        bottomBar->setActive(BottomBar::Side::LEFT, false);

        application->bus.sendUnicast(std::make_shared<TimersProcessingStopMessage>(), service::name::service_time);
    }

    bool PhoneLockedWindow::processLongReleaseEvent(const InputEvent &inputEvent)

D module-apps/apps-common/windows/NoEvents.cpp => module-apps/apps-common/windows/NoEvents.cpp +0 -57
@@ 1,57 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "NoEvents.hpp"
#include "Dialog.hpp"
#include "DialogMetadataMessage.hpp"
#include <log.hpp>

#include <service-appmgr/Controller.hpp>
#include <header/AddElementAction.hpp>
#include <Image.hpp>

using namespace gui;

NoEvents::NoEvents(app::Application *app, const std::string &name) : gui::Dialog(app, name)
{
    header->navigationIndicatorAdd(new gui::header::AddElementAction(), gui::header::BoxSelection::Left);
}

void NoEvents::onBeforeShow(ShowMode mode, SwitchData *data)
{
    Dialog::onBeforeShow(mode, data);
    auto metadata = dynamic_cast<DialogMetadataMessage *>(data);
    if (metadata != nullptr) {
        auto foo      = metadata->get().action;
        inputCallback = [foo](Item &, const InputEvent &inputEvent) -> bool {
            if (foo && inputEvent.isShortRelease(KeyCode::KEY_LEFT)) {
                return foo();
            }
            return false;
        };
    }

    if (getTitle() == utils::translate("app_calendar_title_main")) {
        bottomBar->setActive(gui::BottomBar::Side::LEFT, true);
        bottomBar->setText(gui::BottomBar::Side::LEFT, utils::translate("app_calendar_bar_month"));
    }
}

bool NoEvents::onInput(const gui::InputEvent &inputEvent)
{
    if (inputEvent.isShortRelease(gui::KeyCode::KEY_RF) && getTitle() == utils::translate("app_calendar_title_main")) {
        app::manager::Controller::switchBack(application);
        return true;
    }

    if (AppWindow::onInput(inputEvent)) {
        return true;
    }

    if (inputEvent.isShortRelease(gui::KeyCode::KEY_LF) && getTitle() == utils::translate("app_calendar_title_main")) {
        application->switchWindow(gui::name::window::main_window);
        return true;
    }

    return false;
}

D module-apps/apps-common/windows/NoEvents.hpp => module-apps/apps-common/windows/NoEvents.hpp +0 -29
@@ 1,29 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <functional>
#include "Dialog.hpp"

/// Provides information witch window is equivalent to empty window (DayEvents window and AllEvents window may be empty)
enum class EquivalentWindow
{
    DayEventsWindow,
    AllEventsWindow,
    EmptyWindow
};

namespace gui
{
    class Image;

    class NoEvents : public Dialog
    {
      public:
        NoEvents(app::Application *app, const std::string &name);
        void onBeforeShow(ShowMode mode, SwitchData *data) override;
        bool onInput(const gui::InputEvent &inputEvent) override;
    };

}; // namespace gui

M module-db/CMakeLists.txt => module-db/CMakeLists.txt +0 -14
@@ 24,7 24,6 @@ set(SOURCES
        Databases/CalllogDB.cpp
        Databases/CountryCodesDB.cpp
        Databases/NotificationsDB.cpp
        Databases/EventsDB.cpp

        Tables/Table.cpp
        Tables/SMSTable.cpp


@@ 42,7 41,6 @@ set(SOURCES
        Tables/CountryCodesTable.cpp
        Tables/SMSTemplateTable.cpp
        Tables/NotificationsTable.cpp
        Tables/EventsTable.cpp

        Interface/Record.cpp
        Interface/SMSRecord.cpp


@@ 53,7 51,6 @@ set(SOURCES
        Interface/CalllogRecord.cpp
        Interface/SMSTemplateRecord.cpp
        Interface/NotificationsRecord.cpp
        Interface/EventsRecord.cpp

        queries/RecordQuery.cpp
        queries/messages/sms/QuerySMSSearchByType.cpp


@@ 105,17 102,6 @@ set(SOURCES
        queries/phonebook/QueryContactUpdate.cpp
        queries/phonebook/QueryContactRemove.cpp
        queries/phonebook/QueryNumberGetByID.cpp
        queries/calendar/QueryEventsRemoveICS.cpp
        queries/calendar/QueryEventsRemove.cpp
        queries/calendar/QueryEventsGet.cpp
        queries/calendar/QueryEventsGetAll.cpp
        queries/calendar/QueryEventsAdd.cpp
        queries/calendar/QueryEventsEditICS.cpp
        queries/calendar/QueryEventsEdit.cpp
        queries/calendar/QueryEventsGetFiltered.cpp
        queries/calendar/QueryEventsGetFilteredByDay.cpp
        queries/calendar/QueryEventsGetAllLimited.cpp
        queries/calendar/QueryEventsSelectFirstUpcoming.cpp
        queries/alarms/QueryAlarmsAdd.cpp
        queries/alarms/QueryAlarmsRemove.cpp
        queries/alarms/QueryAlarmsEdit.cpp

D module-db/Databases/EventsDB.cpp => module-db/Databases/EventsDB.cpp +0 -12
@@ 1,12 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventsDB.hpp"

EventsDB::EventsDB(const char *name) : Database(name), events(this)
{
    if (events.create() == false) {
        return;
    }
    isInitialized_ = true;
}

D module-db/Databases/EventsDB.hpp => module-db/Databases/EventsDB.hpp +0 -16
@@ 1,16 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Database/Database.hpp"
#include "module-db/Tables/EventsTable.hpp"

class EventsDB : public Database
{
  public:
    EventsDB(const char *name);
    ~EventsDB() override = default;

    EventsTable events;
};

M module-db/Interface/BaseInterface.hpp => module-db/Interface/BaseInterface.hpp +0 -3
@@ 27,7 27,6 @@ namespace db
            Calllog,
            CountryCodes,
            Notifications,
            Events,
            Quotes,
        };
    };


@@ 54,8 53,6 @@ constexpr const char *c_str(enum db::Interface::Name db)
        return "CountryCodes";
    case db::Interface::Name::Notifications:
        return "Notifications";
    case db::Interface::Name::Events:
        return "Events";
    case db::Interface::Name::Quotes:
        return "Quotes";
    };

D module-db/Interface/EventsRecord.cpp => module-db/Interface/EventsRecord.cpp +0 -460
@@ 1,460 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventsRecord.hpp"
#include "module-db/queries/calendar/QueryEventsGet.hpp"
#include "module-db/queries/calendar/QueryEventsGetAll.hpp"
#include "module-db/queries/calendar/QueryEventsAdd.hpp"
#include "module-db/queries/calendar/QueryEventsRemove.hpp"
#include "module-db/queries/calendar/QueryEventsRemoveICS.hpp"
#include "module-db/queries/calendar/QueryEventsEdit.hpp"
#include <module-db/queries/calendar/QueryEventsEditICS.hpp>
#include <module-db/queries/calendar/QueryEventsGetFiltered.hpp>
#include <module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp>
#include <module-db/queries/calendar/QueryEventsGetAllLimited.hpp>
#include <module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp>
#include <log.hpp>
#include <Utils.hpp>
#include <cassert>
#include <vector>

EventsRecord::EventsRecord(const EventsTableRow &tableRow)
    : Record{tableRow.ID}, UID{tableRow.UID}, title{tableRow.title}, date_from{tableRow.date_from},
      date_till{tableRow.date_till}, reminder{tableRow.reminder}, repeat{tableRow.repeat},
      reminder_fired{tableRow.reminder_fired}, provider_type{tableRow.provider_type}, provider_id{tableRow.provider_id},
      provider_iCalUid{tableRow.provider_iCalUid}
{}

EventsRecordInterface::EventsRecordInterface(EventsDB *eventsDb) : eventsDb(eventsDb)
{}

bool EventsRecordInterface::Add(const EventsRecord &rec)
{
    auto entry = EventsTableRow{{rec.ID},
                                .UID              = rec.UID,
                                .title            = rec.title,
                                .date_from        = rec.date_from,
                                .date_till        = rec.date_till,
                                .reminder         = rec.reminder,
                                .repeat           = rec.repeat,
                                .reminder_fired   = rec.reminder_fired,
                                .provider_type    = rec.provider_type,
                                .provider_id      = rec.provider_id,
                                .provider_iCalUid = rec.provider_iCalUid};

    Repeat repeatOption = Repeat(rec.repeat);
    if (repeatOption > Repeat::yearly) {
        return (eventsDb->events.addCustom(entry));
    }

    switch (repeatOption) {
    case Repeat::never: {
        return eventsDb->events.add(entry);
    }
    case Repeat::daily: {
        return eventsDb->events.addDaily(entry);
    }
    case Repeat::weekly: {
        return eventsDb->events.addWeekly(entry);
    }
    case Repeat::biweekly: {
        return eventsDb->events.addTwoWeeks(entry);
    }
    case Repeat::monthly: {
        return eventsDb->events.addMonth(entry);
    }
    case Repeat::yearly: {
        return eventsDb->events.addYear(entry);
    }
    }

    return false;
}

std::vector<EventsRecord> EventsRecordInterface::Select(TimePoint filter_from,
                                                        TimePoint filter_till,
                                                        uint32_t offset,
                                                        uint32_t limit)
{
    auto rows = eventsDb->events.selectByDatePeriod(filter_from, filter_till, offset, limit);

    auto records = std::vector<EventsRecord>();

    for (auto &r : rows) {
        records.push_back(r);
    }

    return records;
}

std::vector<EventsRecord> EventsRecordInterface::Select(TimePoint date, uint32_t offset, uint32_t limit)
{
    auto rows = eventsDb->events.selectByDate(date, offset, limit);

    auto records = std::vector<EventsRecord>();

    for (auto &r : rows) {
        records.push_back(r);
    }

    return records;
}

std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::GetLimitOffsetByField(uint32_t offset,
                                                                                        uint32_t limit,
                                                                                        EventsRecordField field,
                                                                                        const char *str)
{
    assert(0 && "Not implemented");

    return GetLimitOffset(offset, limit);
}

std::unique_ptr<std::vector<EventsRecord>> EventsRecordInterface::GetLimitOffset(uint32_t offset, uint32_t limit)
{
    if (limit == 0) {
        limit = GetCount();
    }

    auto rows = eventsDb->events.getLimitOffset(offset, limit);

    auto records = std::make_unique<std::vector<EventsRecord>>();

    for (auto &r : rows) {
        records->push_back(r);
    }
    return records;
}

std::vector<EventsRecord> EventsRecordInterface::GetLimitOffsetByDate(uint32_t offset, uint32_t limit)
{
    if (limit == 0) {
        limit = GetCount();
    }

    auto rows = eventsDb->events.getLimitOffsetByDate(offset, limit);

    auto records = std::vector<EventsRecord>();

    for (auto &r : rows) {
        records.push_back(r);
    }

    return records;
}

bool EventsRecordInterface::Update(const EventsRecord &rec)
{
    auto record = eventsDb->events.getById(rec.ID);
    if (!record.isValid()) {
        LOG_DEBUG("Event record is not valid");
        return false;
    }

    auto entry = EventsTableRow{{rec.ID},
                                .UID              = rec.UID,
                                .title            = rec.title,
                                .date_from        = rec.date_from,
                                .date_till        = rec.date_till,
                                .reminder         = rec.reminder,
                                .repeat           = rec.repeat,
                                .reminder_fired   = rec.reminder_fired,
                                .provider_type    = rec.provider_type,
                                .provider_id      = rec.provider_id,
                                .provider_iCalUid = rec.provider_iCalUid};

    bool result = eventsDb->events.update(entry);

    Repeat repeatOption = Repeat(rec.repeat);
    if (repeatOption > Repeat::yearly) {
        return (eventsDb->events.addCustom(entry) && result);
    }

    switch (repeatOption) {
    case Repeat::never: {
        return result;
    }
    case Repeat::daily: {
        return (eventsDb->events.addDaily(entry) && result);
    }
    case Repeat::weekly: {
        return (eventsDb->events.addWeekly(entry) && result);
    }
    case Repeat::biweekly: {
        return (eventsDb->events.addTwoWeeks(entry) && result);
    }
    case Repeat::monthly: {
        return (eventsDb->events.addMonth(entry) && result);
    }
    case Repeat::yearly: {
        return (eventsDb->events.addYear(entry) && result);
    }
    }

    return false;
}

bool EventsRecordInterface::UpdateByUID(const EventsRecord &rec)
{
    auto record = eventsDb->events.getByUID(rec.UID);
    if (!record.isValid()) {
        LOG_DEBUG("Event record is not valid");
        return false;
    }

    auto entry = EventsTableRow{{rec.ID},
                                .UID              = rec.UID,
                                .title            = rec.title,
                                .date_from        = rec.date_from,
                                .date_till        = rec.date_till,
                                .reminder         = rec.reminder,
                                .repeat           = rec.repeat,
                                .reminder_fired   = rec.reminder_fired,
                                .provider_type    = rec.provider_type,
                                .provider_id      = rec.provider_id,
                                .provider_iCalUid = rec.provider_iCalUid};

    bool result = eventsDb->events.updateByUID(entry);

    Repeat repeatOption = Repeat(rec.repeat);
    if (repeatOption > Repeat::yearly) {
        return (eventsDb->events.addCustom(entry) && result);
    }

    switch (Repeat(rec.repeat)) {
    case Repeat::never: {
        return result;
    }
    case Repeat::daily: {
        return (eventsDb->events.addDaily(entry) && result);
    }
    case Repeat::weekly: {
        return (eventsDb->events.addWeekly(entry) && result);
    }
    case Repeat::biweekly: {
        return (eventsDb->events.addTwoWeeks(entry) && result);
    }
    case Repeat::monthly: {
        return (eventsDb->events.addMonth(entry) && result);
    }
    case Repeat::yearly: {
        return (eventsDb->events.addYear(entry) && result);
    }
    }

    return false;
}

bool EventsRecordInterface::RemoveByID(uint32_t id)
{
    return eventsDb->events.removeById(id);
}

bool EventsRecordInterface::RemoveByUID(const std::string &UID)
{
    return eventsDb->events.removeByUID(UID);
}

bool EventsRecordInterface::RemoveByField(EventsRecordField field, const char *str)
{
    assert(0 && "Not implemented");

    return false;
}

EventsRecord EventsRecordInterface::GetByID(uint32_t id)
{
    EventsRecord event{eventsDb->events.getById(id)};
    return event;
}

EventsRecord EventsRecordInterface::GetByUID(const std::string &uid)
{
    EventsRecord event{eventsDb->events.getByUID(uid)};
    return event;
}

uint32_t EventsRecordInterface::GetCount()
{
    return eventsDb->events.count();
}

uint32_t EventsRecordInterface::GetCountFiltered(TimePoint from, TimePoint till)
{
    return eventsDb->events.countFromFilter(from, till);
}

uint32_t EventsRecordInterface::GetCountFilteredByDay(TimePoint filter)
{
    return eventsDb->events.countFromDayFilter(filter);
}

std::vector<EventsRecord> EventsRecordInterface::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
{
    auto rows = eventsDb->events.SelectFirstUpcoming(filter_from, filter_till);

    auto records = std::vector<EventsRecord>();
    for (auto &r : rows) {
        records.push_back(r);
    }

    return records;
}

std::unique_ptr<db::QueryResult> EventsRecordInterface::runQuery(std::shared_ptr<db::Query> query)
{
    if (typeid(*query) == typeid(db::query::events::Get)) {
        return runQueryImplGetResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::GetAll)) {
        return runQueryImplGetAllResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::GetAllLimited)) {
        return runQueryImplGetAllLimitedResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::GetFiltered)) {
        return runQueryImplGetFilteredResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::GetFilteredByDay)) {
        return runQueryImplGetFilteredByDayResult(query);
    }
    if (typeid(*query) == typeid(db::query::events::Add)) {
        return runQueryImplAdd(query);
    }
    if (typeid(*query) == typeid(db::query::events::Remove)) {
        return runQueryImplRemove(query);
    }
    if (typeid(*query) == typeid(db::query::events::RemoveICS)) {
        return runQueryImplRemoveICS(query);
    }
    if (typeid(*query) == typeid(db::query::events::Edit)) {
        return runQueryImplEdit(query);
    }
    if (typeid(*query) == typeid(db::query::events::EditICS)) {
        return runQueryImplEditICS(query);
    }
    if (typeid(*query) == typeid(db::query::events::SelectFirstUpcoming)) {
        return runQueryImplSelectFirstUpcoming(query);
    }
    return nullptr;
}

std::unique_ptr<db::query::events::GetResult> EventsRecordInterface::runQueryImplGetResult(
    std::shared_ptr<db::Query> query)
{
    auto getQuery = static_cast<db::query::events::Get *>(query.get());
    auto records  = GetByID(getQuery->id);
    auto response = std::make_unique<db::query::events::GetResult>(records);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::GetAllResult> EventsRecordInterface::runQueryImplGetAllResult(
    std::shared_ptr<db::Query> query)
{
    auto numberOfEvents = GetCount();
    auto records        = GetLimitOffset(0, numberOfEvents);
    auto response       = std::make_unique<db::query::events::GetAllResult>(*records);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::GetAllLimitedResult> EventsRecordInterface::runQueryImplGetAllLimitedResult(
    std::shared_ptr<db::Query> query)
{

    auto getAllLimitedQuery = static_cast<db::query::events::GetAllLimited *>(query.get());
    auto records            = GetLimitOffsetByDate(getAllLimitedQuery->offset, getAllLimitedQuery->limit);
    auto count              = GetCount();
    auto response           = std::make_unique<db::query::events::GetAllLimitedResult>(std::move(records), count);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::GetFilteredResult> EventsRecordInterface::runQueryImplGetFilteredResult(
    std::shared_ptr<db::Query> query)
{
    auto getFilteredQuery = static_cast<db::query::events::GetFiltered *>(query.get());

    auto records = Select(getFilteredQuery->filter_from,
                          getFilteredQuery->filter_till,
                          getFilteredQuery->offset,
                          getFilteredQuery->limit);

    auto numberOfEvents = GetCountFiltered(getFilteredQuery->filter_from, getFilteredQuery->filter_till);
    auto response       = std::make_unique<db::query::events::GetFilteredResult>(std::move(records), numberOfEvents);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::GetFilteredByDayResult> EventsRecordInterface::runQueryImplGetFilteredByDayResult(
    std::shared_ptr<db::Query> query)
{
    auto getFilteredQuery = static_cast<db::query::events::GetFilteredByDay *>(query.get());

    auto records = Select(getFilteredQuery->filterDate, getFilteredQuery->offset, getFilteredQuery->limit);

    auto numberOfEvents = GetCountFilteredByDay(getFilteredQuery->filterDate);
    auto response = std::make_unique<db::query::events::GetFilteredByDayResult>(std::move(records), numberOfEvents);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::AddResult> EventsRecordInterface::runQueryImplAdd(std::shared_ptr<db::Query> query)
{
    auto addQuery = static_cast<db::query::events::Add *>(query.get());
    bool ret      = Add(addQuery->getRecord());
    auto response = std::make_unique<db::query::events::AddResult>(ret);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::RemoveResult> EventsRecordInterface::runQueryImplRemove(
    std::shared_ptr<db::Query> query)
{
    auto removeQuery = static_cast<db::query::events::Remove *>(query.get());
    bool ret         = RemoveByID(removeQuery->id);
    auto response    = std::make_unique<db::query::events::RemoveResult>(ret);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::RemoveICSResult> EventsRecordInterface::runQueryImplRemoveICS(
    std::shared_ptr<db::Query> query)
{
    auto removeQuery = static_cast<db::query::events::RemoveICS *>(query.get());
    bool ret         = RemoveByUID(removeQuery->UID);
    auto response    = std::make_unique<db::query::events::RemoveICSResult>(ret);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::EditResult> EventsRecordInterface::runQueryImplEdit(std::shared_ptr<db::Query> query)
{
    auto editQuery = static_cast<db::query::events::Edit *>(query.get());
    bool ret       = Update(editQuery->getRecord());
    auto response  = std::make_unique<db::query::events::EditResult>(ret);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::EditICSResult> EventsRecordInterface::runQueryImplEditICS(
    std::shared_ptr<db::Query> query)
{
    auto editQuery = static_cast<db::query::events::EditICS *>(query.get());
    bool ret       = UpdateByUID(editQuery->getRecord());
    auto response  = std::make_unique<db::query::events::EditICSResult>(ret);
    response->setRequestQuery(query);
    return response;
}

std::unique_ptr<db::query::events::SelectFirstUpcomingResult> EventsRecordInterface::runQueryImplSelectFirstUpcoming(
    std::shared_ptr<db::Query> query)
{
    auto getFirstUpcomingQuery = static_cast<db::query::events::SelectFirstUpcoming *>(query.get());

    auto records  = SelectFirstUpcoming(getFirstUpcomingQuery->filter_from, getFirstUpcomingQuery->filter_till);
    auto response = std::make_unique<db::query::events::SelectFirstUpcomingResult>(records);
    response->setRequestQuery(query);
    return response;
}

D module-db/Interface/EventsRecord.hpp => module-db/Interface/EventsRecord.hpp +0 -114
@@ 1,114 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Common/Common.hpp"
#include "module-db/Databases/EventsDB.hpp"
#include "Record.hpp"
#include <utf8/UTF8.hpp>
#include <cstdint>
#include <vector>
#include <variant>
#include <module-apps/application-calendar/data/dateCommon.hpp>

// fw declarations
namespace db::query::events
{
    class Get;
    class GetResult;
    class GetAll;
    class GetAllResult;
    class GetAllLimited;
    class GetAllLimitedResult;
    class GetFiltered;
    class GetFilteredResult;
    class GetFilteredByDay;
    class GetFilteredByDayResult;
    class Add;
    class AddResult;
    class Remove;
    class RemoveResult;
    class RemoveICS;
    class RemoveICSResult;
    class Edit;
    class EditResult;
    class EditICS;
    class EditICSResult;
    class SelectFirstUpcoming;
    class SelectFirstUpcomingResult;
} // namespace db::query::events

struct EventsRecord : public Record
{
    std::string UID;
    std::string title;
    TimePoint date_from;
    TimePoint date_till;
    uint32_t reminder = 0;
    uint32_t repeat   = 0;
    TimePoint reminder_fired;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;

    EventsRecord()  = default;
    ~EventsRecord() = default;
    EventsRecord(const EventsTableRow &tableRow);
};

enum class EventsRecordField
{
    start,
    end,
    reminder
};

class EventsRecordInterface : public RecordInterface<EventsRecord, EventsRecordField>
{
  public:
    EventsRecordInterface(EventsDB *eventsDb);
    ~EventsRecordInterface() override = default;

    bool Add(const EventsRecord &rec) override final;
    bool RemoveByID(uint32_t id) override final;
    bool RemoveByUID(const std::string &UID);
    bool RemoveByField(EventsRecordField field, const char *str) override final;
    bool Update(const EventsRecord &rec) override final;
    bool UpdateByUID(const EventsRecord &rec);
    EventsRecord GetByID(uint32_t id) override final;
    EventsRecord GetByUID(const std::string &uid);
    uint32_t GetCount() override final;
    uint32_t GetCountFiltered(TimePoint from, TimePoint till);
    uint32_t GetCountFilteredByDay(TimePoint filter);
    std::vector<EventsRecord> Select(TimePoint filter_from, TimePoint filter_till, uint32_t offset, uint32_t limit);
    std::vector<EventsRecord> Select(TimePoint date, uint32_t offset, uint32_t limit);
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffset(uint32_t offset, uint32_t limit) override final;
    std::unique_ptr<std::vector<EventsRecord>> GetLimitOffsetByField(uint32_t offset,
                                                                     uint32_t limit,
                                                                     EventsRecordField field,
                                                                     const char *str) override final;
    std::vector<EventsRecord> GetLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::vector<EventsRecord> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);

    std::unique_ptr<db::QueryResult> runQuery(std::shared_ptr<db::Query> query) override;

  private:
    EventsDB *eventsDb = nullptr;

    std::unique_ptr<db::query::events::GetResult> runQueryImplGetResult(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetAllResult> runQueryImplGetAllResult(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetAllLimitedResult> runQueryImplGetAllLimitedResult(
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetFilteredResult> runQueryImplGetFilteredResult(
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::GetFilteredByDayResult> runQueryImplGetFilteredByDayResult(
        std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::AddResult> runQueryImplAdd(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::RemoveResult> runQueryImplRemove(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::RemoveICSResult> runQueryImplRemoveICS(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::EditResult> runQueryImplEdit(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::EditICSResult> runQueryImplEditICS(std::shared_ptr<db::Query> query);
    std::unique_ptr<db::query::events::SelectFirstUpcomingResult> runQueryImplSelectFirstUpcoming(
        std::shared_ptr<db::Query> query);
};

D module-db/Tables/EventsTable.cpp => module-db/Tables/EventsTable.cpp +0 -889
@@ 1,889 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventsTable.hpp"
#include "module-db/Interface/EventsRecord.hpp"
//#include <module-apps/application-calendar/widgets/CalendarStyle.hpp>
//#include <module-apps/application-calendar/data/OptionParser.hpp>

#include <log.hpp>
#include <Utils.hpp>

#include <cassert>

EventsTable::EventsTable(Database *db) : Table(db)
{}

bool EventsTable::create()
{
    if (!db->execute(createTableQuery)) {
        return false;
    }

    return true;
}

bool EventsTable::add(EventsTableRow entry)
{
    // Prevent duplicates using ANDs:
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) "
                       "SELECT '%q','%q', '%q','%q', %lu, %lu, '%q','%q', '%q','%q'"
                       "WHERE NOT EXISTS "
                       "(SELECT 1 FROM events e "
                       "WHERE e.title='%q' "
                       "AND e.date_from='%q' "
                       "AND e.date_till='%q' "
                       "AND e.reminder=%lu "
                       "AND e.repeat=%lu );",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat);
}

bool EventsTable::addDaily(EventsTableRow entry)
{
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{1}).c_str(),
                       TimePointToString(entry.date_till + date::days{1}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{2}).c_str(),
                       TimePointToString(entry.date_till + date::days{2}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{3}).c_str(),
                       TimePointToString(entry.date_till + date::days{3}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{4}).c_str(),
                       TimePointToString(entry.date_till + date::days{4}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{5}).c_str(),
                       TimePointToString(entry.date_till + date::days{5}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{6}).c_str(),
                       TimePointToString(entry.date_till + date::days{6}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addWeekly(EventsTableRow entry)
{
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{7}).c_str(),
                       TimePointToString(entry.date_till + date::days{7}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{14}).c_str(),
                       TimePointToString(entry.date_till + date::days{14}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{21}).c_str(),
                       TimePointToString(entry.date_till + date::days{21}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addTwoWeeks(EventsTableRow entry)
{
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{14}).c_str(),
                       TimePointToString(entry.date_till + date::days{14}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{28}).c_str(),
                       TimePointToString(entry.date_till + date::days{28}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from + date::days{42}).c_str(),
                       TimePointToString(entry.date_till + date::days{42}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addMonth(EventsTableRow entry)
{
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{1}).c_str(),
                       TimePointToString(entry.date_till, date::months{1}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{2}).c_str(),
                       TimePointToString(entry.date_till, date::months{2}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{3}).c_str(),
                       TimePointToString(entry.date_till, date::months{3}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{4}).c_str(),
                       TimePointToString(entry.date_till, date::months{4}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{5}).c_str(),
                       TimePointToString(entry.date_till, date::months{5}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{6}).c_str(),
                       TimePointToString(entry.date_till, date::months{6}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{7}).c_str(),
                       TimePointToString(entry.date_till, date::months{7}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{8}).c_str(),
                       TimePointToString(entry.date_till, date::months{8}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{9}).c_str(),
                       TimePointToString(entry.date_till, date::months{9}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{10}).c_str(),
                       TimePointToString(entry.date_till, date::months{10}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{11}).c_str(),
                       TimePointToString(entry.date_till, date::months{11}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::months{12}).c_str(),
                       TimePointToString(entry.date_till, date::months{12}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

bool EventsTable::addYear(EventsTableRow entry)
{
    if (entry.UID.empty()) {
        entry.UID = createUID();
    }
    return db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                       "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q'),"
                       "('%q','%q', '%q','%q', %u, %u,'%q', '%q', '%q', '%q');",
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::years{1}).c_str(),
                       TimePointToString(entry.date_till, date::years{1}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::years{2}).c_str(),
                       TimePointToString(entry.date_till, date::years{2}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::years{3}).c_str(),
                       TimePointToString(entry.date_till, date::years{3}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str(),
                       entry.title.c_str(),
                       TimePointToString(entry.date_from, date::years{4}).c_str(),
                       TimePointToString(entry.date_till, date::years{4}).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str());
}

std::vector<bool> EventsTable::parseOptions(uint32_t repeatOptionValue)
{
    const uint32_t startBit        = 16;
    const uint32_t numberOfOptions = 7;
    std::vector<bool> weekDayOptions;
    for (uint32_t i = startBit; i < startBit + numberOfOptions; i++) {
        weekDayOptions.push_back(false);
    }
    for (uint32_t i = startBit; i < startBit + numberOfOptions; i++) {
        if (repeatOptionValue & (1 << i)) {
            LOG_DEBUG("Set option array %d", static_cast<int>(i));
            weekDayOptions[i - startBit] = true;
        }
    }
    return weekDayOptions;
}

bool EventsTable::addCustom(EventsTableRow entry)
{
    // OptionParser parser;
    const uint32_t numberOfWeeks    = 4;
    const uint32_t numberOfWeekDays = 7;
    bool result                     = true;
    std::vector<bool> weekDayOptions;
    weekDayOptions          = parseOptions(entry.repeat);
    uint32_t incrementation = 0;

    result = result && db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, reminder, repeat, "
                                   "reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                                   "('%q','%q', '%q','%q', %u, %u, '%q','%q', '%q','%q');",
                                   entry.UID.c_str(),
                                   entry.title.c_str(),
                                   TimePointToString(entry.date_from).c_str(),
                                   TimePointToString(entry.date_till).c_str(),
                                   entry.reminder,
                                   entry.repeat,
                                   TimePointToString(entry.reminder_fired).c_str(),
                                   entry.provider_type.c_str(),
                                   entry.provider_id.c_str(),
                                   entry.provider_iCalUid.c_str());

    auto dateFrom = getFirstWeekDay(entry.date_from);
    auto dateTill = getFirstWeekDay(entry.date_till);
    for (uint32_t i = 1; i <= numberOfWeeks; i++) {
        for (auto option : weekDayOptions) {
            if (option) {
                LOG_DEBUG("start: %s", TimePointToString(dateFrom + date::days{incrementation}).c_str());
                LOG_DEBUG("start: %s", TimePointToString(dateTill + date::days{incrementation}).c_str());
                result =
                    result &&
                    db->execute("INSERT or IGNORE INTO events (uid, title, date_from, date_till, "
                                "reminder, repeat, reminder_fired, provider_type, provider_id, provider_iCalUid) VALUES"
                                "('%q','%q', '%q','%q', %u, %u, '%q','%q', '%q','%q');",
                                entry.UID.c_str(),
                                entry.title.c_str(),
                                TimePointToString(dateFrom + date::days{incrementation}).c_str(),
                                TimePointToString(dateTill + date::days{incrementation}).c_str(),
                                entry.reminder,
                                entry.repeat,
                                TimePointToString(entry.reminder_fired).c_str(),
                                entry.provider_type.c_str(),
                                entry.provider_id.c_str(),
                                entry.provider_iCalUid.c_str());
            }
            ++incrementation;
        }
        incrementation = i * numberOfWeekDays;
    }
    return result;
}

bool EventsTable::removeById(uint32_t id)
{
    return db->execute("DELETE FROM events where _id = %u;", id);
}

bool EventsTable::removeByUID(const std::string &UID)
{
    return db->execute("DELETE FROM events where uid = '%q';", UID.c_str());
}

bool EventsTable::removeByField(EventsTableFields field, const char *str)
{
    std::string fieldName;

    switch (field) {
    case EventsTableFields::date_from: {
        fieldName = "date_from";
        break;
    }
    case EventsTableFields::date_till: {
        fieldName = "date_till";
        break;
    }
    }

    return db->execute("DELETE FROM events where %q = '%q';", fieldName.c_str(), str);
}

bool EventsTable::update(EventsTableRow entry)
{
    return db->execute("UPDATE events SET title= '%q', date_from = '%q', date_till = '%q', reminder "
                       "= %u, repeat = %u, reminder_fired = '%q', provider_type = '%q', provider_id = '%q', "
                       "provider_iCalUid = '%q' WHERE _id = %u;",
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       entry.repeat,
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.ID);
}

bool EventsTable::updateByUID(EventsTableRow entry)
{
    return db->execute("UPDATE events SET title= '%q', date_from = '%q', date_till = '%q', reminder "
                       "= %u, repeat = %u, reminder_fired = '%q', provider_type = '%q', provider_id = '%q', "
                       "provider_iCalUid = '%q' WHERE uid = '%q';",
                       entry.title.c_str(),
                       TimePointToString(entry.date_from).c_str(),
                       TimePointToString(entry.date_till).c_str(),
                       entry.reminder,
                       static_cast<uint32_t>(entry.repeat),
                       TimePointToString(entry.reminder_fired).c_str(),
                       entry.provider_type.c_str(),
                       entry.provider_id.c_str(),
                       entry.provider_iCalUid.c_str(),
                       entry.UID.c_str());
}

EventsTableRow EventsTable::getById(uint32_t id)
{
    auto retQuery = db->query("SELECT * FROM events WHERE _id= %u;", id);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return EventsTableRow();
    }

    assert(retQuery->getRowCount() == 1);

    return EventsTableRow{
        (*retQuery)[0].getUInt32(),                              // ID
        (*retQuery)[1].getString(),                              // UID
        (*retQuery)[2].getString(),                              // title
        TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
        TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
        (*retQuery)[5].getUInt32(),                              // reminder
        (*retQuery)[6].getUInt32(),                              // repeat
        TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
        (*retQuery)[8].getString(),                              // provider type
        (*retQuery)[9].getString(),                              // provider id
        (*retQuery)[10].getString()                              // provider iCalUid
    };
}

EventsTableRow EventsTable::getByUID(const std::string &UID)
{
    auto retQuery = db->query("SELECT * FROM events WHERE uid= '%q';", UID.c_str());

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return EventsTableRow();
    }

    assert(retQuery->getRowCount() == 1);

    return EventsTableRow{
        (*retQuery)[0].getUInt32(),                              // ID
        (*retQuery)[1].getString(),                              // UID
        (*retQuery)[2].getString(),                              // title
        TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
        TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
        (*retQuery)[5].getUInt32(),                              // reminder
        (*retQuery)[6].getUInt32(),                              // repeat
        TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
        (*retQuery)[8].getString(),                              // provider type
        (*retQuery)[9].getString(),                              // provider id
        (*retQuery)[10].getString()                              // provider iCalUid
    };
}

std::vector<EventsTableRow> EventsTable::selectByDatePeriod(TimePoint filterFrom,
                                                            TimePoint filterTill,
                                                            uint32_t offset,
                                                            uint32_t limit)
{
    auto retQuery = db->query(
        "SELECT * FROM events WHERE ( date('%q') >= date_from AND date_from > date('%q', 'start of day') ) OR "
        " ( date('%q') >= date_till AND date_till > date('%q', 'start of day') ) OR "
        " ( date_till > date('%q') AND date('%q', 'start of day') > date_from ) ORDER BY datetime(date_from) LIMIT %u "
        "OFFSET %u;",
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        TimePointToString(filterFrom).c_str(),
        TimePointToString(filterTill).c_str(),
        limit,
        offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();
    }

    std::vector<EventsTableRow> ret;

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[0].getUInt32(),                              // ID
            (*retQuery)[1].getString(),                              // UID
            (*retQuery)[2].getString(),                              // title
            TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid
        });

    } while (retQuery->nextRow());

    return ret;
}

std::vector<EventsTableRow> EventsTable::selectByDate(TimePoint dateFilter, uint32_t offset, uint32_t limit)
{
    auto dateFilterFrom = dateFilter + date::days{1};
    auto retQuery       = db->query(
        "SELECT * FROM events WHERE date_from < date('%q', 'start of day') AND date('%q', 'start of day') <= date_till "
        "ORDER BY datetime(date_from) LIMIT %u OFFSET %u;",
        TimePointToString(dateFilterFrom).c_str(),
        TimePointToString(dateFilter).c_str(),
        limit,
        offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();
    }

    std::vector<EventsTableRow> ret;

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[0].getUInt32(),                              // ID
            (*retQuery)[1].getString(),                              // UID
            (*retQuery)[2].getString(),                              // title
            TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid
        });

    } while (retQuery->nextRow());

    return ret;
}

std::vector<EventsTableRow> EventsTable::getLimitOffset(uint32_t offset, uint32_t limit)
{
    auto retQuery = db->query("SELECT * from events LIMIT %lu OFFSET %lu;", limit, offset);
    //"SELECT * FROM event WHERE id = "

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();
    }

    std::vector<EventsTableRow> ret;

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[0].getUInt32(),                              // ID
            (*retQuery)[1].getString(),                              // UID
            (*retQuery)[2].getString(),                              // title
            TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid

        });
    } while (retQuery->nextRow());

    return ret;
}

std::vector<EventsTableRow> EventsTable::getLimitOffsetByDate(uint32_t offset, uint32_t limit)
{
    auto retQuery = db->query("SELECT * from events ORDER BY datetime(date_from) LIMIT %u OFFSET %u;", limit, offset);

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();
    }

    std::vector<EventsTableRow> ret;

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[0].getUInt32(),                              // ID
            (*retQuery)[1].getString(),                              // UID
            (*retQuery)[2].getString(),                              // title
            TimePointFromString((*retQuery)[3].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_till
            (*retQuery)[5].getUInt32(),                              // reminder
            (*retQuery)[6].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[7].getString().c_str()), // reminder_fired
            (*retQuery)[8].getString(),                              // provider type
            (*retQuery)[9].getString(),                              // provider id
            (*retQuery)[10].getString()                              // provider iCalUid
        });
    } while (retQuery->nextRow());

    return ret;
}

std::vector<EventsTableRow> EventsTable::getLimitOffsetByField(uint32_t offset,
                                                               uint32_t limit,
                                                               EventsTableFields field,
                                                               const char *str)
{
    std::vector<EventsTableRow> ret;

    assert(0 && "Not implemented");

    return ret;
}

bool EventsTable::drop()
{
    return db->execute("DROP TABLE events;");
}

uint32_t EventsTable::count()
{
    auto queryRet = db->query("SELECT COUNT(*) FROM events;");

    if (queryRet == nullptr || queryRet->getRowCount() == 0) {
        return 0;
    }

    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countFromFilter(TimePoint from, TimePoint till)
{
    auto queryRet = db->query(
        "SELECT COUNT(*) FROM events WHERE date('%q') <= date_from AND date_from < date('%q', 'start of day');",
        TimePointToString(from).c_str(),
        TimePointToString(till).c_str());

    if (queryRet == nullptr || queryRet->getRowCount() == 0) {
        return 0;
    }

    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countFromDayFilter(TimePoint filter)
{
    auto filterFrom = filter + date::days{1};
    auto queryRet   = db->query("SELECT COUNT(*) FROM events WHERE date_from < datetime('%q', 'start of day') AND "
                              "datetime('%q', 'start of day') <= date_till;",
                              TimePointToString(filterFrom).c_str(),
                              TimePointToString(filter).c_str());

    if (queryRet == nullptr || queryRet->getRowCount() == 0) {
        return 0;
    }

    return (*queryRet)[0].getUInt32();
}

uint32_t EventsTable::countByFieldId(const char *field, uint32_t id)
{
    assert(0 && "Not implemented");

    return 0;
}

std::vector<EventsTableRow> EventsTable::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
{
    auto retQuery = db->query("SELECT DATETIME(date_from, '-' || reminder || ' minutes') AS calc_dt, * "
                              "FROM events "
                              "WHERE calc_dt >= '%q' "
                              "AND reminder_fired = '%q' "
                              "AND reminder <> -1 "
                              "ORDER BY calc_dt "
                              "LIMIT 1 ",
                              TimePointToString(filter_from).c_str(),
                              TimePointToString(TIME_POINT_INVALID).c_str());

    if (retQuery == nullptr || retQuery->getRowCount() == 0) {
        return std::vector<EventsTableRow>();
    }

    std::vector<EventsTableRow> ret;

    do {
        ret.push_back(EventsTableRow{
            (*retQuery)[1].getUInt32(),                              // ID
            (*retQuery)[2].getString(),                              // UID
            (*retQuery)[3].getString(),                              // title
            TimePointFromString((*retQuery)[4].getString().c_str()), // date_from
            TimePointFromString((*retQuery)[5].getString().c_str()), // date_till
            (*retQuery)[6].getUInt32(),                              // reminder
            (*retQuery)[7].getUInt32(),                              // repeat
            TimePointFromString((*retQuery)[8].getString().c_str()), // reminder_fired
            (*retQuery)[9].getString(),                              // provider type
            (*retQuery)[10].getString(),                             // provider id
            (*retQuery)[11].getString()                              // provider iCalUid
        });
    } while (retQuery->nextRow());

    return ret;
}

D module-db/Tables/EventsTable.hpp => module-db/Tables/EventsTable.hpp +0 -90
@@ 1,90 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Table.hpp"
#include "Record.hpp"
#include <module-db/Database/Database.hpp>
#include <module-db/Common/Common.hpp>
#include <utf8/UTF8.hpp>
#include <module-apps/application-calendar/data/dateCommon.hpp>

struct EventsTableRow : public Record
{
    std::string UID;
    std::string title;
    TimePoint date_from      = TIME_POINT_INVALID;
    TimePoint date_till      = TIME_POINT_INVALID;
    uint32_t reminder        = 0;
    uint32_t repeat          = 0;
    TimePoint reminder_fired = TIME_POINT_INVALID;
    std::string provider_type;
    std::string provider_id;
    std::string provider_iCalUid;
};

enum class EventsTableFields
{
    date_from,
    date_till
};

class EventsTable : public Table<EventsTableRow, EventsTableFields>
{
  public:
    EventsTable(Database *db);
    ~EventsTable() override = default;

    bool create() override final;
    bool add(EventsTableRow entry) override final;
    bool addDaily(EventsTableRow entry);
    bool addWeekly(EventsTableRow entry);
    bool addTwoWeeks(EventsTableRow entry);
    bool addMonth(EventsTableRow entry);
    bool addYear(EventsTableRow entry);
    std::vector<bool> parseOptions(uint32_t repeatOptionValue);
    bool addCustom(EventsTableRow entry);
    bool removeById(uint32_t id) override final;
    bool removeByUID(const std::string &UID);
    bool removeByField(EventsTableFields field, const char *str) override final;
    bool update(EventsTableRow entry) override final;
    bool updateByUID(EventsTableRow entry);
    bool drop();
    EventsTableRow getByUID(const std::string &UID);
    EventsTableRow getById(uint32_t id) override final;
    std::vector<EventsTableRow> selectByDatePeriod(TimePoint filter_from,
                                                   TimePoint filter_till,
                                                   uint32_t offset,
                                                   uint32_t limit);

    std::vector<EventsTableRow> selectByDate(TimePoint date, uint32_t offset, uint32_t limit);

    uint32_t count() override final;
    uint32_t countFromFilter(TimePoint from, TimePoint till);
    uint32_t countFromDayFilter(TimePoint filter);
    uint32_t countByFieldId(const char *field, uint32_t id) override final;
    std::vector<EventsTableRow> getLimitOffset(uint32_t offset, uint32_t limit) override final;
    std::vector<EventsTableRow> getLimitOffsetByField(uint32_t offset,
                                                      uint32_t limit,
                                                      EventsTableFields field,
                                                      const char *str) override final;

    std::vector<EventsTableRow> getLimitOffsetByDate(uint32_t offset, uint32_t limit);
    std::vector<EventsTableRow> SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);

  private:
    const char *createTableQuery = "CREATE TABLE IF NOT EXISTS events("
                                   "_id INTEGER PRIMARY KEY AUTOINCREMENT,"
                                   "uid TEXT,"
                                   "title TEXT,"
                                   "date_from DATETIME,"
                                   "date_till DATETIME,"
                                   "reminder INTEGER,"
                                   "repeat INTEGER,"
                                   "reminder_fired DATETIME,"
                                   "provider_type TEXT,"
                                   "provider_id TEXT,"
                                   "provider_iCalUid TEXT,"
                                   "UNIQUE (title, date_from, date_till));";
};

D module-db/queries/calendar/QueryEventsAdd.cpp => module-db/queries/calendar/QueryEventsAdd.cpp +0 -33
@@ 1,33 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsAdd.hpp"

namespace db::query::events
{
    Add::Add(EventsRecord record) : Query(Query::Type::Create), record(record)
    {}

    auto Add::getRecord() const -> EventsRecord
    {
        return record;
    }

    auto Add::debugInfo() const -> std::string
    {
        return "Add";
    }

    AddResult::AddResult(bool ret) : ret(ret)
    {}

    auto AddResult::getResult() const -> bool
    {
        return ret;
    }

    auto AddResult::debugInfo() const -> std::string
    {
        return "AddResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsAdd.hpp => module-db/queries/calendar/QueryEventsAdd.hpp +0 -34
@@ 1,34 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{
    class Add : public Query
    {
        EventsRecord record;

      public:
        Add(EventsRecord record);
        [[nodiscard]] auto getRecord() const -> EventsRecord;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class AddResult : public QueryResult
    {
      public:
        explicit AddResult(bool ret);
        [[nodiscard]] auto getResult() const -> bool;

        [[nodiscard]] auto debugInfo() const -> std::string override;

        const bool ret = true;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsEdit.cpp => module-db/queries/calendar/QueryEventsEdit.cpp +0 -33
@@ 1,33 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsEdit.hpp"

namespace db::query::events
{
    Edit::Edit(EventsRecord record) : Query(Query::Type::Update), record(record)
    {}

    auto Edit::getRecord() const -> EventsRecord
    {
        return record;
    }

    auto Edit::debugInfo() const -> std::string
    {
        return "Edit";
    }

    EditResult::EditResult(bool ret) : ret(ret)
    {}

    auto EditResult::getResult() const -> bool
    {
        return ret;
    }

    auto EditResult::debugInfo() const -> std::string
    {
        return "EditResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsEdit.hpp => module-db/queries/calendar/QueryEventsEdit.hpp +0 -34
@@ 1,34 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{
    class Edit : public Query
    {
        EventsRecord record;

      public:
        Edit(EventsRecord record);

        [[nodiscard]] auto getRecord() const -> EventsRecord;
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class EditResult : public QueryResult
    {
      public:
        explicit EditResult(bool ret);
        [[nodiscard]] auto getResult() const -> bool;

        [[nodiscard]] auto debugInfo() const -> std::string override;

        const bool ret = true;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsEditICS.cpp => module-db/queries/calendar/QueryEventsEditICS.cpp +0 -33
@@ 1,33 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsEditICS.hpp"

namespace db::query::events
{
    EditICS::EditICS(EventsRecord record) : Query(Query::Type::Update), record(record)
    {}

    auto EditICS::getRecord() const -> EventsRecord
    {
        return record;
    }

    auto EditICS::debugInfo() const -> std::string
    {
        return "Edit";
    }

    EditICSResult::EditICSResult(bool ret) : ret(ret)
    {}

    auto EditICSResult::getResult() const -> bool
    {
        return ret;
    }

    auto EditICSResult::debugInfo() const -> std::string
    {
        return "EditResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsEditICS.hpp => module-db/queries/calendar/QueryEventsEditICS.hpp +0 -34
@@ 1,34 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{
    class EditICS : public Query
    {
        EventsRecord record;

      public:
        EditICS(EventsRecord record);

        [[nodiscard]] auto getRecord() const -> EventsRecord;
        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class EditICSResult : public QueryResult
    {
      public:
        explicit EditICSResult(bool ret);
        [[nodiscard]] auto getResult() const -> bool;

        [[nodiscard]] auto debugInfo() const -> std::string override;

        bool ret;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGet.cpp => module-db/queries/calendar/QueryEventsGet.cpp +0 -28
@@ 1,28 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsGet.hpp"

namespace db::query::events
{
    Get::Get(const uint32_t &id) : Query(Query::Type::Read), id(id)
    {}

    auto Get::debugInfo() const -> std::string
    {
        return "Get";
    }

    GetResult::GetResult(EventsRecord record) : record(record)
    {}

    auto GetResult::getResult() const -> EventsRecord
    {
        return record;
    }

    auto GetResult::debugInfo() const -> std::string
    {
        return "GetResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGet.hpp => module-db/queries/calendar/QueryEventsGet.hpp +0 -32
@@ 1,32 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{
    class Get : public Query
    {
      public:
        const uint32_t id;
        Get(const uint32_t &id);

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class GetResult : public QueryResult
    {
        EventsRecord record;

      public:
        GetResult(EventsRecord record);
        [[nodiscard]] auto getResult() const -> EventsRecord;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetAll.cpp => module-db/queries/calendar/QueryEventsGetAll.cpp +0 -28
@@ 1,28 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsGetAll.hpp"

namespace db::query::events
{
    GetAll::GetAll() : Query(Query::Type::Read)
    {}

    auto GetAll::debugInfo() const -> std::string
    {
        return "GetAll";
    }

    GetAllResult::GetAllResult(std::vector<EventsRecord> records) : records{std::move(records)}
    {}

    auto GetAllResult::getResult() -> std::vector<EventsRecord>
    {
        return records;
    }

    auto GetAllResult::debugInfo() const -> std::string
    {
        return "GetAllResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetAll.hpp => module-db/queries/calendar/QueryEventsGetAll.hpp +0 -31
@@ 1,31 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{
    class GetAll : public Query
    {
      public:
        GetAll();

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

    class GetAllResult : public QueryResult
    {
        std::vector<EventsRecord> records;

      public:
        GetAllResult(std::vector<EventsRecord> records);
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetAllLimited.cpp => module-db/queries/calendar/QueryEventsGetAllLimited.cpp +0 -35
@@ 1,35 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsGetAllLimited.hpp"

namespace db::query::events
{
    GetAllLimited::GetAllLimited(const uint32_t &offset, const uint32_t &limit)
        : Query(Query::Type::Read), offset(offset), limit(limit)
    {}

    auto GetAllLimited::debugInfo() const -> std::string
    {
        return "GetAllLimited";
    }

    GetAllLimitedResult::GetAllLimitedResult(std::vector<EventsRecord> records, uint32_t count)
        : records{std::move(records)}, recordsCount{count}
    {}

    auto GetAllLimitedResult::getResult() -> std::vector<EventsRecord>
    {
        return records;
    }

    auto GetAllLimitedResult::getCountResult() const noexcept -> uint32_t
    {
        return recordsCount;
    }

    auto GetAllLimitedResult::debugInfo() const -> std::string
    {
        return "GetAllLimitedResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetAllLimited.hpp => module-db/queries/calendar/QueryEventsGetAllLimited.hpp +0 -35
@@ 1,35 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <module-db/Interface/EventsRecord.hpp>
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{
    class GetAllLimited : public Query
    {
      public:
        GetAllLimited(const uint32_t &offset, const uint32_t &limit);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        uint32_t offset = 0;
        uint32_t limit  = 0;
    };

    class GetAllLimitedResult : public QueryResult
    {
        std::vector<EventsRecord> records;
        uint32_t recordsCount;

      public:
        GetAllLimitedResult(std::vector<EventsRecord> records, uint32_t count);
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;
        [[nodiscard]] auto getCountResult() const noexcept -> uint32_t;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetFiltered.cpp => module-db/queries/calendar/QueryEventsGetFiltered.cpp +0 -35
@@ 1,35 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsGetFiltered.hpp"

namespace db::query::events
{
    GetFiltered::GetFiltered(TimePoint filter_from, TimePoint filter_till, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), filter_from(filter_from), filter_till(filter_till), offset(offset), limit(limit)
    {}

    auto GetFiltered::debugInfo() const -> std::string
    {
        return "GetFiltered";
    }

    GetFilteredResult::GetFilteredResult(std::vector<EventsRecord> records, uint32_t count)
        : records{std::move(records)}, recordsCount{count}
    {}

    auto GetFilteredResult::getResult() -> std::vector<EventsRecord>
    {
        return records;
    }

    auto GetFilteredResult::getCountResult() -> uint32_t
    {
        return recordsCount;
    }

    auto GetFilteredResult::debugInfo() const -> std::string
    {
        return "GetFilteredResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetFiltered.hpp => module-db/queries/calendar/QueryEventsGetFiltered.hpp +0 -38
@@ 1,38 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>
#include <module-apps/application-calendar/data/dateCommon.hpp>

namespace db::query::events
{
    class GetFiltered : public Query
    {
      public:
        GetFiltered(TimePoint filter_from, TimePoint filter_till, uint32_t offset = 0, uint32_t limit = UINT32_MAX);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        TimePoint filter_from;
        TimePoint filter_till;
        uint32_t offset;
        uint32_t limit;
    };

    class GetFilteredResult : public QueryResult
    {
        std::vector<EventsRecord> records;
        uint32_t recordsCount;

      public:
        GetFilteredResult(std::vector<EventsRecord> records, uint32_t count);
        auto getCountResult() -> uint32_t;
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetFilteredByDay.cpp => module-db/queries/calendar/QueryEventsGetFilteredByDay.cpp +0 -35
@@ 1,35 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsGetFilteredByDay.hpp"

namespace db::query::events
{
    GetFilteredByDay::GetFilteredByDay(TimePoint filterDate, uint32_t offset, uint32_t limit)
        : Query(Query::Type::Read), filterDate(filterDate), offset(offset), limit(limit)
    {}

    auto GetFilteredByDay::debugInfo() const -> std::string
    {
        return "GetFilteredByDay";
    }

    GetFilteredByDayResult::GetFilteredByDayResult(const std::vector<EventsRecord> &records, uint32_t count)
        : records{std::move(records)}, recordsCount{count}
    {}

    auto GetFilteredByDayResult::getResult() -> std::vector<EventsRecord>
    {
        return records;
    }

    auto GetFilteredByDayResult::getCountResult() -> uint32_t
    {
        return recordsCount;
    }

    auto GetFilteredByDayResult::debugInfo() const -> std::string
    {
        return "GetFilteredByDayResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp => module-db/queries/calendar/QueryEventsGetFilteredByDay.hpp +0 -37
@@ 1,37 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>
#include <module-apps/application-calendar/data/dateCommon.hpp>

namespace db::query::events
{
    class GetFilteredByDay : public Query
    {
      public:
        GetFilteredByDay(TimePoint filterDate, uint32_t offset = 0, uint32_t limit = UINT32_MAX);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        TimePoint filterDate;
        uint32_t offset;
        uint32_t limit;
    };

    class GetFilteredByDayResult : public QueryResult
    {
        std::vector<EventsRecord> records;
        uint32_t recordsCount;

      public:
        GetFilteredByDayResult(const std::vector<EventsRecord> &records, uint32_t count);
        auto getCountResult() -> uint32_t;
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsRemove.cpp => module-db/queries/calendar/QueryEventsRemove.cpp +0 -28
@@ 1,28 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsRemove.hpp"

namespace db::query::events
{
    Remove::Remove(const uint32_t &id) : Query(Query::Type::Delete), id(id)
    {}

    auto Remove::debugInfo() const -> std::string
    {
        return "Remove";
    }

    RemoveResult::RemoveResult(bool ret) : ret(ret)
    {}

    auto RemoveResult::getResult() const -> bool
    {
        return ret;
    }

    auto RemoveResult::debugInfo() const -> std::string
    {
        return "RemoveResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsRemove.hpp => module-db/queries/calendar/QueryEventsRemove.hpp +0 -33
@@ 1,33 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{

    class Remove : public Query
    {
      public:
        Remove(const uint32_t &id);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        uint32_t id;
    };

    class RemoveResult : public QueryResult
    {
        bool ret;

      public:
        explicit RemoveResult(bool ret);
        [[nodiscard]] auto getResult() const -> bool;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsRemoveICS.cpp => module-db/queries/calendar/QueryEventsRemoveICS.cpp +0 -28
@@ 1,28 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsRemoveICS.hpp"

namespace db::query::events
{
    RemoveICS::RemoveICS(const std::string &UID) : Query(Query::Type::Delete), UID(UID)
    {}

    auto RemoveICS::debugInfo() const -> std::string
    {
        return "Remove";
    }

    RemoveICSResult::RemoveICSResult(bool ret) : ret(ret)
    {}

    auto RemoveICSResult::getResult() const -> bool
    {
        return ret;
    }

    auto RemoveICSResult::debugInfo() const -> std::string
    {
        return "RemoveResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsRemoveICS.hpp => module-db/queries/calendar/QueryEventsRemoveICS.hpp +0 -33
@@ 1,33 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>

namespace db::query::events
{

    class RemoveICS : public Query
    {
      public:
        RemoveICS(const std::string &UID);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        std::string UID;
    };

    class RemoveICSResult : public QueryResult
    {
        bool ret;

      public:
        explicit RemoveICSResult(bool ret);
        [[nodiscard]] auto getResult() const -> bool;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

D module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.cpp +0 -30
@@ 1,30 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "QueryEventsSelectFirstUpcoming.hpp"

namespace db::query::events
{
    SelectFirstUpcoming::SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till)
        : Query(Query::Type::Read), filter_from(filter_from), filter_till(filter_till)
    {}

    auto SelectFirstUpcoming::debugInfo() const -> std::string
    {
        return "SelectFirstUpcoming";
    }

    SelectFirstUpcomingResult::SelectFirstUpcomingResult(std::vector<EventsRecord> records)
        : records{std::move(records)}
    {}

    auto SelectFirstUpcomingResult::getResult() -> std::vector<EventsRecord>
    {
        return records;
    }

    auto SelectFirstUpcomingResult::debugInfo() const -> std::string
    {
        return "SelectFirstUpcomingResult";
    }
} // namespace db::query::events

D module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp => module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp +0 -36
@@ 1,36 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "module-db/Interface/EventsRecord.hpp"
#include <Common/Query.hpp>
#include <string>
#include <module-apps/application-calendar/data/dateCommon.hpp>

namespace db::query::events
{
    /// Query for getting calendar event whose reminder is first in filtered time range
    class SelectFirstUpcoming : public Query
    {
      public:
        SelectFirstUpcoming(TimePoint filter_from, TimePoint filter_till);
        [[nodiscard]] auto debugInfo() const -> std::string override;

        TimePoint filter_from;
        TimePoint filter_till;
    };

    /// Result of SelectFirstUpcoming query
    class SelectFirstUpcomingResult : public QueryResult
    {
        std::vector<EventsRecord> records;

      public:
        SelectFirstUpcomingResult(std::vector<EventsRecord> records);
        [[nodiscard]] auto getResult() -> std::vector<EventsRecord>;

        [[nodiscard]] auto debugInfo() const -> std::string override;
    };

} // namespace db::query::events

M module-db/tests/CMakeLists.txt => module-db/tests/CMakeLists.txt +0 -2
@@ 18,8 18,6 @@ add_catch2_executable(
        ContactsRingtonesTable_tests.cpp
        ContactsTable_tests.cpp
        DbInitializer.cpp
        EventsRecord_tests.cpp
        #EventsTable_tests.cpp
        NotesRecord_tests.cpp
        NotesTable_tests.cpp
        NotificationsRecord_tests.cpp

D module-db/tests/EventsRecord_tests.cpp => module-db/tests/EventsRecord_tests.cpp +0 -1039
@@ 1,1039 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "common.hpp"

#include <Interface/EventsRecord.hpp>
#include <Database/Database.hpp>
#include <Databases/EventsDB.hpp>
#include <module-db/queries/calendar/QueryEventsGet.hpp>
#include <module-db/queries/calendar/QueryEventsGetAll.hpp>
#include <module-db/queries/calendar/QueryEventsGetAllLimited.hpp>
#include <module-db/queries/calendar/QueryEventsGetFiltered.hpp>
#include <module-db/queries/calendar/QueryEventsAdd.hpp>
#include <module-db/queries/calendar/QueryEventsRemove.hpp>
#include <module-db/queries/calendar/QueryEventsRemoveICS.hpp>
#include <module-db/queries/calendar/QueryEventsEdit.hpp>
#include <module-db/queries/calendar/QueryEventsEditICS.hpp>
#include <module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp>

#include <catch2/catch.hpp>

#include <algorithm>
#include <cstdint>
#include <filesystem>
#include <iostream>

using namespace std::chrono_literals;

static auto remove_events(EventsDB &db) -> bool
{
    auto count   = db.events.count();
    auto records = db.events.getLimitOffset(0, count);
    bool ret     = true;
    for (auto rec : records) {
        ret = ret && db.events.removeById(rec.ID);
    }
    return ret;
}

TEST_CASE("Events Record tests")
{
    Database::initialize();

    const auto eventsPath = (std::filesystem::path{"sys/user"} / "events.db");
    RemoveDbFiles(eventsPath.stem());

    EventsDB eventsDb{eventsPath.c_str()};

    REQUIRE(eventsDb.isInitialized());

    EventsTableRow testRow = {{1},
                              .UID              = "test1",
                              .title            = "Event1",
                              .date_from        = TimePointFromString("2019-10-20 14:25:00"),
                              .date_till        = TimePointFromString("2019-10-20 15:36:00"),
                              .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                              .repeat           = static_cast<uint32_t>(Repeat::never),
                              .reminder_fired   = TimePointFromString("2019-10-20 14:20:00"),
                              .provider_type    = "PurePhone",
                              .provider_id      = "testID",
                              .provider_iCalUid = "test6"};

    EventsTableRow testRow2 = {{2},
                               .UID              = "test2",
                               .title            = "Event2",
                               .date_from        = TimePointFromString("2019-10-21 14:24:00"),
                               .date_till        = TimePointFromString("2019-10-21 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TIME_POINT_INVALID,
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow3 = {{3},
                               .UID              = "test3",
                               .title            = "Event3",
                               .date_from        = TimePointFromString("2019-10-22 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-22 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-22 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow4 = {{4},
                               .UID              = "test4",
                               .title            = "Event4",
                               .date_from        = TimePointFromString("2019-10-23 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-23 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-23 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow5 = {{5},
                               .UID              = "test5",
                               .title            = "Event5",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TIME_POINT_INVALID,
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow6 = {{6},
                               .UID              = "test6",
                               .title            = "Event6",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    auto check_record = [](const EventsRecord &actual, const EventsRecord &expected) {
        CHECK(actual.UID == expected.UID);
        CHECK(actual.title == expected.title);
        CHECK(TimePointToString(actual.date_from) == TimePointToString(expected.date_from));
        CHECK(TimePointToString(actual.date_till) == TimePointToString(expected.date_till));
        CHECK(actual.reminder == expected.reminder);
        CHECK(actual.repeat == expected.repeat);
        CHECK(actual.reminder_fired == expected.reminder_fired);
        CHECK(actual.provider_type == expected.provider_type);
        CHECK(actual.provider_id == expected.provider_id);
        CHECK(actual.provider_iCalUid == expected.provider_iCalUid);
        CHECK(actual.isValid());
    };


    EventsRecordInterface eventsRecordInterface(&eventsDb);
    auto numberOfEvents = eventsRecordInterface.GetCount();
    REQUIRE(numberOfEvents == 0);

    auto testRecord  = EventsRecord(testRow);
    auto testRecord2 = EventsRecord(testRow2);
    auto testRecord3 = EventsRecord(testRow3);
    auto testRecord4 = EventsRecord(testRow4);
    auto testRecord5 = EventsRecord(testRow5);
    auto testRecord6 = EventsRecord(testRow6);

    REQUIRE(eventsRecordInterface.Add(testRecord));
    REQUIRE(eventsRecordInterface.Add(testRecord2));
    REQUIRE(eventsRecordInterface.Add(testRecord3));
    REQUIRE(eventsRecordInterface.Add(testRecord4));
    REQUIRE(eventsRecordInterface.Add(testRecord5));
    REQUIRE(eventsRecordInterface.Add(testRecord6));

    numberOfEvents = eventsRecordInterface.GetCount();
    REQUIRE(numberOfEvents == 6);

    ///====TEST QUERY====
    auto getQuery = [&](uint32_t id, EventsRecord expected_record) {
        auto query  = std::make_shared<db::query::events::Get>(id);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto record = result->getResult();
        check_record(record, expected_record);
    };

    [[maybe_unused]] auto getAll = [&](std::vector<EventsRecord> expected_records, uint32_t expectedAllRecsNumber) {
        auto query  = std::make_shared<db::query::events::GetAll>();
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetAllResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records.size() == expectedAllRecsNumber);

        auto expected_record = expected_records.begin();
        std::for_each(records.begin(), records.end(), [&expected_record](const EventsRecord &record) {
            CHECK(record.ID == expected_record->ID);
            CHECK(record.UID == expected_record->UID);
            CHECK(record.title == expected_record->title);
            CHECK(TimePointToString(record.date_from) == TimePointToString(expected_record->date_from));
            CHECK(TimePointToString(record.date_till) == TimePointToString(expected_record->date_till));
            CHECK(record.reminder == expected_record->reminder);
            CHECK(record.repeat == expected_record->repeat);
            CHECK(record.reminder_fired == expected_record->reminder_fired);
            CHECK(record.provider_type == expected_record->provider_type);
            CHECK(record.provider_id == expected_record->provider_id);
            CHECK(record.provider_iCalUid == expected_record->provider_iCalUid);
            CHECK(record.isValid());
            expected_record++;
        });
    };

    auto getFiltered = [&](TimePoint filter_from, TimePoint filter_till, std::vector<EventsRecord> expected_records) {
        auto query  = std::make_shared<db::query::events::GetFiltered>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetFilteredResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records.size() == expected_records.size());
        auto count = result->getCountResult();
        REQUIRE(count == expected_records.size());

        auto expected_record = expected_records.begin();
        std::for_each(records.begin(), records.end(), [&expected_record](const EventsRecord &record) {
            CHECK(record.ID == expected_record->ID);
            CHECK(record.UID == expected_record->UID);
            CHECK(record.title == expected_record->title);
            CHECK(TimePointToString(record.date_from) == TimePointToString(expected_record->date_from));
            CHECK(TimePointToString(record.date_till) == TimePointToString(expected_record->date_till));
            CHECK(record.reminder == expected_record->reminder);
            CHECK(record.repeat == expected_record->repeat);
            CHECK(record.reminder_fired == expected_record->reminder_fired);
            CHECK(record.provider_type == expected_record->provider_type);
            CHECK(record.provider_id == expected_record->provider_id);
            CHECK(record.provider_iCalUid == expected_record->provider_iCalUid);
            CHECK(record.isValid());
            expected_record++;
        });
    };

    auto getAllLimited = [&](uint32_t offset,
                             uint32_t limit,
                             std::vector<EventsRecord> expected_records,
                             uint32_t expectedAllRecsNumber) {
        auto query  = std::make_shared<db::query::events::GetAllLimited>(offset, limit);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::GetAllLimitedResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        REQUIRE(records.size() == limit);
        auto count = result->getCountResult();
        CHECK(count == expectedAllRecsNumber);

        auto expected_record = expected_records.begin();
        std::for_each(records.begin(), records.end(), [&expected_record](const EventsRecord &record) {
            CHECK(record.ID == expected_record->ID);
            CHECK(record.UID == expected_record->UID);
            CHECK(record.title == expected_record->title);
            CHECK(TimePointToString(record.date_from) == TimePointToString(expected_record->date_from));
            CHECK(TimePointToString(record.date_till) == TimePointToString(expected_record->date_till));
            CHECK(record.reminder == expected_record->reminder);
            CHECK(record.repeat == expected_record->repeat);
            CHECK(record.reminder_fired == expected_record->reminder_fired);
            CHECK(record.provider_type == expected_record->provider_type);
            CHECK(record.provider_id == expected_record->provider_id);
            CHECK(record.provider_iCalUid == expected_record->provider_iCalUid);
            CHECK(record.isValid());
            expected_record++;
        });
    };

    auto AddQuery = [&](EventsRecord record) {
        auto query  = std::make_shared<db::query::events::Add>(record);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::AddResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());

        auto entry = eventsRecordInterface.GetByID(record.ID);
        check_record(entry, record);
    };

    auto RemoveQueryICS = [&](std::string uid) {
        auto query  = std::make_shared<db::query::events::RemoveICS>(uid);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::RemoveICSResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());
        REQUIRE_NOTHROW(eventsRecordInterface.GetByUID(uid));
    };

    auto EditQuery = [&](uint32_t id, EventsRecord record) {
        auto entryToUpdate             = eventsRecordInterface.GetByID(id);
        entryToUpdate.title            = record.title;
        entryToUpdate.date_from        = record.date_from;
        entryToUpdate.date_till        = record.date_till;
        entryToUpdate.reminder         = record.reminder;
        entryToUpdate.repeat           = record.repeat;
        entryToUpdate.provider_type    = record.provider_type;
        entryToUpdate.provider_id      = record.provider_id;
        entryToUpdate.provider_iCalUid = record.provider_iCalUid;

        auto query  = std::make_shared<db::query::events::Edit>(entryToUpdate);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::EditResult *>(ret.get());
        REQUIRE(result != nullptr);

        auto entry = eventsRecordInterface.GetByID(entryToUpdate.ID);
        record.ID  = entry.ID;
        record.UID = entry.UID;
        check_record(entry, record);
    };

    auto RemoveQuery = [&](uint32_t id) {
        auto query  = std::make_shared<db::query::events::Remove>(id);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::RemoveResult *>(ret.get());
        REQUIRE(result != nullptr);
        REQUIRE(result->getResult());
        REQUIRE_NOTHROW(eventsRecordInterface.GetByID(id));
    };

    auto EditQueryICS = [&](std::string uid, EventsRecord recordUpdateData) {
        auto entryToUpdate             = eventsRecordInterface.GetByUID(uid);
        entryToUpdate.title            = recordUpdateData.title;
        entryToUpdate.date_from        = recordUpdateData.date_from;
        entryToUpdate.date_till        = recordUpdateData.date_till;
        entryToUpdate.reminder         = recordUpdateData.reminder;
        entryToUpdate.repeat           = recordUpdateData.repeat;
        entryToUpdate.provider_type    = recordUpdateData.provider_type;
        entryToUpdate.provider_id      = recordUpdateData.provider_id;
        entryToUpdate.provider_iCalUid = recordUpdateData.provider_iCalUid;

        auto query  = std::make_shared<db::query::events::EditICS>(entryToUpdate);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::EditICSResult *>(ret.get());
        REQUIRE(result != nullptr);

        auto entry           = eventsRecordInterface.GetByID(entryToUpdate.ID);
        recordUpdateData.ID  = entry.ID;
        recordUpdateData.UID = entry.UID;
        check_record(entry, recordUpdateData);
    };

    [[maybe_unused]] auto selectFirstUpcomingEvent = [&](TimePoint filter_from, TimePoint filter_till) {
        auto query  = std::make_shared<db::query::events::SelectFirstUpcoming>(filter_from, filter_till);
        auto ret    = eventsRecordInterface.runQuery(query);
        auto result = dynamic_cast<db::query::events::SelectFirstUpcomingResult *>(ret.get());
        REQUIRE(result != nullptr);
        auto records = result->getResult();
        return records;
    };

    SECTION("Default Constructor")
    {
        EventsRecord testRec;
        REQUIRE(testRec.title == "");
        REQUIRE(testRec.reminder == 0);
        REQUIRE(testRec.repeat == 0);
    }

    SECTION("Constructor from EventsTableRow")
    {
        EventsRecord testRecord(testRow);
        check_record(testRecord, testRow);
    }

    SECTION("Get entry by ID")
    {
        auto entry = eventsRecordInterface.GetByID(testRecord3.ID);
        check_record(entry, testRecord3);
    }

    SECTION("Get count filtered")
    {
        auto count = eventsRecordInterface.GetCountFiltered(testRecord3.date_from, testRecord5.date_from);
        CHECK(count == 2);
    }

    SECTION("Get count")
    {
        auto count = eventsRecordInterface.GetCount();
        CHECK(count == 6);
    }

    SECTION("Get entry - invalid ID")
    {
        auto entry = eventsRecordInterface.GetByID(100);
        CHECK(entry.ID == DB_ID_NONE);
        CHECK(entry.title == "");
        CHECK(entry.date_from == TIME_POINT_INVALID);
        CHECK(entry.date_till == TIME_POINT_INVALID);
        CHECK(entry.reminder == 0);
        CHECK(entry.repeat == 0);
        CHECK(entry.reminder_fired == TIME_POINT_INVALID);
        CHECK(entry.provider_type == "");
        CHECK(entry.provider_id == "");
        CHECK(entry.provider_iCalUid == "");
        CHECK_FALSE(entry.isValid());
    }

    SECTION("Entry remove")
    {
        REQUIRE(eventsRecordInterface.RemoveByID(testRecord4.ID));
        numberOfEvents = eventsRecordInterface.GetCount();
        REQUIRE(numberOfEvents == 5);
        CHECK_NOTHROW(eventsRecordInterface.GetByID(testRecord4.ID));
    }

    SECTION("Entry remove by uid")
    {
        REQUIRE(eventsRecordInterface.RemoveByUID(testRecord4.UID));
        numberOfEvents = eventsRecordInterface.GetCount();
        REQUIRE(numberOfEvents == 5);
        CHECK_NOTHROW(eventsRecordInterface.GetByID(testRecord4.ID));
    }

    SECTION("Entry update")
    {
        auto entryToUpdate      = eventsRecordInterface.GetByID(testRecord5.ID);
        entryToUpdate.title     = "newTitle";
        entryToUpdate.date_from = TimePointFromString("2019-12-31 23:59:00");
        entryToUpdate.date_till = TimePointFromString("2019-12-31 23:59:00");
        REQUIRE(eventsRecordInterface.Update(entryToUpdate));

        auto entry = eventsRecordInterface.GetByID(entryToUpdate.ID);
        check_record(entry, entryToUpdate);
    }

    SECTION("Entry update by uid")
    {
        auto entryToUpdate      = eventsRecordInterface.GetByID(testRecord3.ID);
        entryToUpdate.title     = "newTitle";
        entryToUpdate.date_from = TimePointFromString("2019-12-31 23:59:00");
        entryToUpdate.date_till = TimePointFromString("2019-12-31 23:59:00");
        REQUIRE(eventsRecordInterface.UpdateByUID(entryToUpdate));

        auto entry = eventsRecordInterface.GetByID(entryToUpdate.ID);
        check_record(entry, entryToUpdate);
    }

    SECTION("Get entries")
    {

        SECTION("Get records using valid offset/limit parameters")
        {
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffset(0, numberOfEvents);
            REQUIRE(retOffsetLimit->size() == numberOfEvents);
            CHECK((*retOffsetLimit)[0].ID == testRecord.ID);
            CHECK((*retOffsetLimit)[1].ID == testRecord2.ID);
            CHECK((*retOffsetLimit)[2].ID == testRecord3.ID);
            CHECK((*retOffsetLimit)[3].ID == testRecord4.ID);
            CHECK((*retOffsetLimit)[4].ID == testRecord5.ID);
            CHECK((*retOffsetLimit)[5].ID == testRecord6.ID);
        }

        SECTION("Get table rows using bigger limit parameters")
        {
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffset(0, 100);
            REQUIRE(retOffsetLimit->size() == numberOfEvents);
        }

        SECTION("Get table rows using invalid offset/limit parameters(should return empty object)")
        {
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffset(6, 10);
            REQUIRE(retOffsetLimit->size() == 0);
        }

        SECTION("0 - get all")
        {
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(retOffsetLimit->size() == numberOfEvents);
        }

        SECTION("Get table rows by SELECT")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2019-10-19 14:24:00"), TimePointFromString("2019-10-27 15:36:00"), 0, UINT32_MAX);
            REQUIRE(retOffsetLimit.size() == numberOfEvents);
        }

        SECTION("Get table rows by SELECT limited")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2019-10-19 14:24:00"), TimePointFromString("2019-10-27 15:36:00"), 0, 3);
            REQUIRE(retOffsetLimit.size() == 3);
        }

        SECTION("Get table rows by SELECT limited by date")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2019-10-19 14:24:00"), TimePointFromString("2019-10-22 15:36:00"), 0, UINT32_MAX);
            REQUIRE(retOffsetLimit.size() == 2);
        }

        SECTION("Get table rows by SELECT invalid")
        {
            auto retOffsetLimit = eventsRecordInterface.Select(
                TimePointFromString("2010-10-20 14:24:00"), TimePointFromString("2010-10-20 15:36:00"), 0, UINT32_MAX);
            REQUIRE(retOffsetLimit.size() == 0);
        }

        SECTION("Get records using valid offset/limit date parameters")
        {
            auto recordWithEarliestDate      = eventsRecordInterface.GetByID(testRecord3.ID);
            recordWithEarliestDate.date_from = TimePointFromString("1990-10-23 14:25:00");
            REQUIRE(eventsRecordInterface.Update(recordWithEarliestDate));
            auto retOffsetLimit = eventsRecordInterface.GetLimitOffsetByDate(0, numberOfEvents);
            REQUIRE(retOffsetLimit.size() == numberOfEvents);
            CHECK((retOffsetLimit)[0].ID == testRecord3.ID);
            CHECK((retOffsetLimit)[1].ID == testRecord.ID);
            CHECK((retOffsetLimit)[2].ID == testRecord2.ID);
            CHECK((retOffsetLimit)[3].ID == testRecord4.ID);
            CHECK((retOffsetLimit)[4].ID == testRecord5.ID);
            CHECK((retOffsetLimit)[5].ID == testRecord6.ID);
        }

        SECTION("Get records using valid offset/limit date parameters")
        {
            auto recordWithEarliestDate      = eventsRecordInterface.GetByID(testRecord3.ID);
            recordWithEarliestDate.date_from = TimePointFromString("1990-10-23 14:25:00");
            recordWithEarliestDate.date_till = TimePointFromString("1990-10-23 14:25:00");
            REQUIRE(eventsRecordInterface.Update(recordWithEarliestDate));

            auto retOffsetLimit = eventsRecordInterface.GetLimitOffsetByDate(0, numberOfEvents);
            REQUIRE(retOffsetLimit.size() == numberOfEvents);
            CHECK((retOffsetLimit)[0].ID == recordWithEarliestDate.ID);
            CHECK((retOffsetLimit)[1].ID == testRecord.ID);
            CHECK((retOffsetLimit)[2].ID == testRecord2.ID);
            CHECK((retOffsetLimit)[3].ID == testRecord4.ID);
            CHECK((retOffsetLimit)[4].ID == testRecord5.ID);
            CHECK((retOffsetLimit)[5].ID == testRecord6.ID);
        }
    }

    SECTION("Entry add repeated")
    {
        if (eventsRecordInterface.GetCount() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsRecordInterface.GetCount() == 0);

        uint32_t testRecordActualId = numberOfEvents + 1;
        testRecord3.ID              = testRecordActualId;
        auto expectedRecordData     = testRecord3;

        auto set_repeat = [&](Repeat option, uint32_t count) {
            expectedRecordData.repeat = static_cast<uint32_t>(option);
            REQUIRE(eventsRecordInterface.Add(expectedRecordData));
            auto entries = eventsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(entries->size() == count);
            return *entries;
        };

        SECTION("Daily, count = 7")
        {
            uint32_t expectedEventsCount = 7;
            auto entries                 = set_repeat(Repeat::daily, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + 24h;
                expectedRecordData.date_till = expectedRecordData.date_till + 24h;
            }
        }

        SECTION("Weekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::weekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (7 * 24h);
            }
        }

        SECTION("Biweekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::biweekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (2 * 7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (2 * 7 * 24h);
            }
        }

        SECTION("Monthly, count = 12")
        {
            uint32_t expectedEventsCount = 12;
            expectedRecordData.date_from = TimePointFromString("2019-01-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-01-05 17:00:00");
            auto entries                 = set_repeat(Repeat::monthly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-02-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-05 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-03-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-03-05 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-04-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-04-05 17:00:00");
            check_record(entries[3], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-05-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-05-05 17:00:00");
            check_record(entries[4], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-06-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-06-05 17:00:00");
            check_record(entries[5], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-07-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-07-05 17:00:00");
            check_record(entries[6], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-08-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-08-05 17:00:00");
            check_record(entries[7], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-09-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-09-05 17:00:00");
            check_record(entries[8], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-10-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-10-05 17:00:00");
            check_record(entries[9], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-11-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-11-05 17:00:00");
            check_record(entries[10], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-12-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-12-05 17:00:00");
            check_record(entries[11], expectedRecordData);
        }

        SECTION("Yearly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            expectedRecordData.date_from = TimePointFromString("2019-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-28 17:00:00");
            auto entries                 = set_repeat(Repeat::yearly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2021-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2021-02-28 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2022-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2022-02-28 17:00:00");
            check_record(entries[3], expectedRecordData);
        }
    }

    SECTION("Entry update repeated")
    {
        if (eventsRecordInterface.GetCount() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsRecordInterface.GetCount() == 0);
        uint32_t testRecordActualId = numberOfEvents + 1;
        testRecord.ID               = testRecordActualId;
        REQUIRE(eventsRecordInterface.Add(testRecord));

        auto expectedRecordData = eventsRecordInterface.GetByID(testRecord.ID);

        auto set_repeat = [&](Repeat option, uint32_t count) {
            expectedRecordData.repeat = static_cast<uint32_t>(option);
            REQUIRE(eventsRecordInterface.Update(expectedRecordData));
            auto entries = eventsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(entries->size() == count);
            return *entries;
        };

        SECTION("Daily, count = 7")
        {
            uint32_t expectedEventsCount = 7;
            auto entries                 = set_repeat(Repeat::daily, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + 24h;
                expectedRecordData.date_till = expectedRecordData.date_till + 24h;
            }
        }

        SECTION("Weekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::weekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (7 * 24h);
            }
        }

        SECTION("Biweekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::biweekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (2 * 7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (2 * 7 * 24h);
            }
        }

        SECTION("Monthly, count = 12")
        {
            uint32_t expectedEventsCount = 12;
            expectedRecordData.date_from = TimePointFromString("2019-01-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-01-05 17:00:00");
            auto entries                 = set_repeat(Repeat::monthly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-02-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-05 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-03-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-03-05 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-04-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-04-05 17:00:00");
            check_record(entries[3], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-05-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-05-05 17:00:00");
            check_record(entries[4], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-06-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-06-05 17:00:00");
            check_record(entries[5], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-07-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-07-05 17:00:00");
            check_record(entries[6], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-08-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-08-05 17:00:00");
            check_record(entries[7], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-09-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-09-05 17:00:00");
            check_record(entries[8], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-10-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-10-05 17:00:00");
            check_record(entries[9], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-11-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-11-05 17:00:00");
            check_record(entries[10], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-12-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-12-05 17:00:00");
            check_record(entries[11], expectedRecordData);
        }

        SECTION("Yearly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            expectedRecordData.date_from = TimePointFromString("2019-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-28 17:00:00");
            auto entries                 = set_repeat(Repeat::yearly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2021-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2021-02-28 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2022-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2022-02-28 17:00:00");
            check_record(entries[3], expectedRecordData);
        }
    }

    SECTION("Entry updateICS repeated")
    {
        if (eventsRecordInterface.GetCount() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsRecordInterface.GetCount() == 0);

        REQUIRE(eventsRecordInterface.Add(testRecord));
        auto expectedRecordData = eventsRecordInterface.GetByUID(testRecord.UID);

        auto set_repeat = [&](Repeat option, uint32_t count) {
            expectedRecordData.repeat = static_cast<uint32_t>(option);
            REQUIRE(eventsRecordInterface.UpdateByUID(expectedRecordData));
            auto entries = eventsRecordInterface.GetLimitOffset(0, 0);
            REQUIRE(entries->size() == count);
            return *entries;
        };

        SECTION("Daily, count = 7")
        {
            uint32_t expectedEventsCount = 7;
            auto entries                 = set_repeat(Repeat::daily, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + 24h;
                expectedRecordData.date_till = expectedRecordData.date_till + 24h;
            }
        }

        SECTION("Weekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::weekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (7 * 24h);
            }
        }

        SECTION("Biweekly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            auto entries                 = set_repeat(Repeat::biweekly, expectedEventsCount);

            for (auto entry : entries) {
                check_record(entry, expectedRecordData);
                expectedRecordData.date_from = expectedRecordData.date_from + (2 * 7 * 24h);
                expectedRecordData.date_till = expectedRecordData.date_till + (2 * 7 * 24h);
            }
        }

        SECTION("Monthly, count = 12")
        {
            uint32_t expectedEventsCount = 12;
            expectedRecordData.date_from = TimePointFromString("2019-01-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-01-05 17:00:00");
            auto entries                 = set_repeat(Repeat::monthly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-02-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-05 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-03-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-03-05 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-04-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-04-05 17:00:00");
            check_record(entries[3], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-05-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-05-05 17:00:00");
            check_record(entries[4], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-06-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-06-05 17:00:00");
            check_record(entries[5], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-07-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-07-05 17:00:00");
            check_record(entries[6], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-08-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-08-05 17:00:00");
            check_record(entries[7], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-09-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-09-05 17:00:00");
            check_record(entries[8], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-10-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-10-05 17:00:00");
            check_record(entries[9], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-11-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-11-05 17:00:00");
            check_record(entries[10], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2019-12-05 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-12-05 17:00:00");
            check_record(entries[11], expectedRecordData);
        }

        SECTION("Yearly, count = 4")
        {
            uint32_t expectedEventsCount = 4;
            expectedRecordData.date_from = TimePointFromString("2019-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2019-02-28 17:00:00");
            auto entries                 = set_repeat(Repeat::yearly, expectedEventsCount);
            check_record(entries[0], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2020-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2020-02-28 17:00:00");
            check_record(entries[1], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2021-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2021-02-28 17:00:00");
            check_record(entries[2], expectedRecordData);

            expectedRecordData.date_from = TimePointFromString("2022-02-28 15:00:00");
            expectedRecordData.date_till = TimePointFromString("2022-02-28 17:00:00");
            check_record(entries[3], expectedRecordData);
        }
    }

    SECTION("Select first upcoming event")
    {
        TimePoint start_date = TimePointFromString("2019-10-19 14:24:00");
        auto nextUpcoming    = eventsRecordInterface.SelectFirstUpcoming(start_date, start_date);
        REQUIRE(nextUpcoming.size() == 1);
        EventsRecord nextEventsRecord = nextUpcoming.at(0);
        REQUIRE(nextEventsRecord.ID == 2);
        nextEventsRecord.reminder_fired = TimePointFromString("2018-12-31 23:59:00");
        REQUIRE(eventsRecordInterface.Update(nextEventsRecord));

        start_date   = TimePointFromString("2019-10-23 14:24:00");
        nextUpcoming = eventsRecordInterface.SelectFirstUpcoming(start_date, start_date);
        REQUIRE(nextUpcoming.size() == 1);
        REQUIRE(nextEventsRecord.ID == 2);
    }

    SECTION("Check Get via query")
    {
        getQuery(testRecord.ID, testRecord);
        getQuery(testRecord3.ID, testRecord3);
        getQuery(testRecord6.ID, testRecord6);
    }

    SECTION("Get All via query")
    {
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord);
        expectedRecords.push_back(testRecord2);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        expectedRecords.push_back(testRecord6);
        getAll(expectedRecords, numberOfEvents);
    }

    SECTION("GetFiltered via query")
    {
        auto startDate = testRecord2.date_from;
        auto endDate   = testRecord5.date_till;
        std::vector<EventsRecord> expectedFilteredRecords;
        expectedFilteredRecords.push_back(testRecord2);
        expectedFilteredRecords.push_back(testRecord3);
        expectedFilteredRecords.push_back(testRecord4);
        getFiltered(startDate, endDate, expectedFilteredRecords);
    }

    SECTION("Get All Limited via query")
    {
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord2);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        uint32_t limit = 4;
        getAllLimited(testRecord2.ID - 1, limit, expectedRecords, numberOfEvents);
    }

    SECTION("Add via query")
    {
        EventsTableRow testRow7 = {{7},
                                   .UID              = "test7",
                                   .title            = "Event7",
                                   .date_from        = TimePointFromString("2019-11-24 14:25:00"),
                                   .date_till        = TimePointFromString("2019-12-24 15:36:00"),
                                   .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                                   .repeat           = static_cast<uint32_t>(Repeat::never),
                                   .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                                   .provider_type    = "PurePhone",
                                   .provider_id      = "testID",
                                   .provider_iCalUid = "test6"};

        auto testRecord7 = EventsRecord(testRow7);
        AddQuery(testRecord7);
    }

    SECTION("Remove via query")
    {
        RemoveQuery(testRecord2.ID);
        REQUIRE_NOTHROW(eventsRecordInterface.GetByID(testRecord2.ID));
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        expectedRecords.push_back(testRecord6);
        getAll(expectedRecords, numberOfEvents - 1);
    }

    SECTION("RemoveICS via query")
    {
        RemoveQueryICS(testRecord2.UID);
        REQUIRE_NOTHROW(eventsRecordInterface.GetByUID(testRecord2.UID));
        std::vector<EventsRecord> expectedRecords;
        expectedRecords.push_back(testRecord);
        expectedRecords.push_back(testRecord3);
        expectedRecords.push_back(testRecord4);
        expectedRecords.push_back(testRecord5);
        expectedRecords.push_back(testRecord6);
        getAll(expectedRecords, numberOfEvents - 1);
    }

    SECTION("Update via query")
    {
        EditQuery(testRecord.ID, testRecord);
        EditQuery(testRecord3.ID, testRecord3);
        EditQuery(testRecord6.ID, testRecord6);
    }

    SECTION("Update ICS via query")
    {
        EditQueryICS(testRecord.UID, testRecord);
        EditQueryICS(testRecord3.UID, testRecord3);
        EditQueryICS(testRecord6.UID, testRecord6);
    }

    SECTION("Select first upcoming via query")
    {
        auto date_from = TimePointFromString("2019-10-19 14:24:00");
        auto date_till = TimePointFromString("2026-10-20 14:24:00");
        auto records   = selectFirstUpcomingEvent(date_from, date_till);
        REQUIRE(records.size() == 1);
        auto firstEvent = records.at(0);
        REQUIRE(firstEvent.ID == 2);

        date_from = TimePointFromString("2025-11-10 14:24:00");
        date_till = TimePointFromString("2026-10-20 14:24:00");
        records   = selectFirstUpcomingEvent(date_from, date_till);
        REQUIRE(records.size() == 0);
    }

    Database::deinitialize();
}

D module-db/tests/EventsTable_tests.cpp => module-db/tests/EventsTable_tests.cpp +0 -838
@@ 1,838 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>

#include "Database/Database.hpp"
#include "Databases/EventsDB.hpp"
#include "Tables/EventsTable.hpp"

#include <purefs/filesystem_paths.hpp>
#include <stdint.h>
#include <string>
#include <algorithm>
#include <iostream>
#include <purefs/filesystem_paths.hpp>
#include <unistd.h>

using namespace std::chrono_literals;

static auto remove_events(EventsDB &db) -> bool
{
    auto count   = db.events.count();
    auto records = db.events.getLimitOffset(0, count);
    bool ret     = true;
    for (auto rec : records) {
        ret = ret && db.events.removeById(rec.ID);
    }
    return ret;
}

TEST_CASE("Events Table tests")
{
    Database::initialize();

    const auto eventsPath = (std::filesystem::path{"sys/user"} / "events.db");
    if (std::filesystem::exists(eventsPath)) {
        REQUIRE(std::filesystem::remove(eventsPath));
    }

    EventsDB eventsDb{eventsPath.c_str()};
    REQUIRE(eventsDb.isInitialized());

    auto &eventsTbl = eventsDb.events;

    if (eventsTbl.count() > 0) {
        REQUIRE(remove_events(eventsDb));
    }

    REQUIRE(eventsTbl.count() == 0);

    SECTION("Default Constructor")
    {
        EventsTableRow testRow;
        CHECK(testRow.ID == DB_ID_NONE);
        CHECK(testRow.UID == "");
        CHECK(testRow.title == "");
        CHECK(testRow.date_from == TIME_POINT_INVALID);
        CHECK(testRow.date_till == TIME_POINT_INVALID);
        CHECK(testRow.reminder == 0);
        CHECK(testRow.repeat == 0);
        CHECK(testRow.reminder_fired == TIME_POINT_INVALID);
        CHECK(testRow.provider_id == "");
        CHECK_FALSE(testRow.isValid());
    }

    EventsTableRow testRow1 = {{1},
                               .UID              = "test1",
                               .title            = "Event1",
                               .date_from        = TimePointFromString("2019-10-20 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-20 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-20 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow2 = {{2},
                               .UID              = "test2",
                               .title            = "Event2",
                               .date_from        = TimePointFromString("2019-10-21 14:24:00"),
                               .date_till        = TimePointFromString("2019-10-21 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-21 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow3 = {{3},
                               .UID              = "test3",
                               .title            = "Event3",
                               .date_from        = TimePointFromString("2019-10-22 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-22 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-22 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow4 = {{4},
                               .UID              = "test4",
                               .title            = "Event4",
                               .date_from        = TimePointFromString("2019-10-23 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-23 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-23 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow5 = {{5},
                               .UID              = "test5",
                               .title            = "Event5",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    EventsTableRow testRow6 = {{6},
                               .UID              = "test6",
                               .title            = "Event6",
                               .date_from        = TimePointFromString("2019-10-24 14:25:00"),
                               .date_till        = TimePointFromString("2019-10-24 15:36:00"),
                               .reminder         = static_cast<uint32_t>(Reminder::five_min_before),
                               .repeat           = static_cast<uint32_t>(Repeat::never),
                               .reminder_fired   = TimePointFromString("2019-10-24 14:20:00"),
                               .provider_type    = "PurePhone",
                               .provider_id      = "testID",
                               .provider_iCalUid = "test6"};

    /// add
    CHECK(eventsTbl.add(testRow1));
    CHECK(eventsTbl.add(testRow2));
    CHECK(eventsTbl.add(testRow3));
    CHECK(eventsTbl.add(testRow4));
    CHECK(eventsTbl.add(testRow5));
    CHECK(eventsTbl.add(testRow6));
    CHECK(eventsTbl.count() == 6);

    auto check_tableRow = [](const EventsTableRow &actual, const EventsTableRow &expected) {
        CHECK(actual.ID == expected.ID);
        CHECK(actual.UID == expected.UID);
        CHECK(actual.title == expected.title);
        CHECK(TimePointToString(actual.date_from) == TimePointToString(expected.date_from));
        CHECK(TimePointToString(actual.date_till) == TimePointToString(expected.date_till));
        CHECK(actual.reminder == expected.reminder);
        CHECK(actual.repeat == expected.repeat);
        CHECK(actual.reminder_fired == expected.reminder_fired);
        CHECK(actual.provider_type == expected.provider_type);
        CHECK(actual.provider_id == expected.provider_id);
        CHECK(actual.provider_iCalUid == expected.provider_iCalUid);
        CHECK(actual.isValid());
    };

    SECTION("Check add daily")
    {
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 7;
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addDaily(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-19 10:00:00"), TimePointFromString("2020-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(startDate));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(endDate));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            startDate += 24h;
            endDate += 24h;
        }
    }

    SECTION("Check add weekly")
    {
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addWeekly(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-19 10:00:00"), TimePointFromString("2020-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(startDate));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(endDate));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            startDate += (24h * 7);
            endDate += (24h * 7);
        }
    }

    SECTION("Check add biweekly")
    {
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        TimePoint startDate     = TimePointFromString("2019-10-20 14:30:00");
        TimePoint endDate       = TimePointFromString("2019-10-20 15:30:00");
        testRow1.date_from      = startDate;
        testRow1.date_till      = endDate;
        CHECK(eventsTbl.addTwoWeeks(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-19 10:00:00"), TimePointFromString("2020-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(startDate));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(endDate));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            startDate += (24h * 7 * 2);
            endDate += (24h * 7 * 2);
        }
    }

    SECTION("Check add monthly")
    {
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 12;
        const std::array<TimePoint, 24> dates{
            TimePointFromString("2019-01-20 14:30:00"), TimePointFromString("2019-01-20 15:30:00"),
            TimePointFromString("2019-02-20 14:30:00"), TimePointFromString("2019-02-20 15:30:00"),
            TimePointFromString("2019-03-20 14:30:00"), TimePointFromString("2019-03-20 15:30:00"),
            TimePointFromString("2019-04-20 14:30:00"), TimePointFromString("2019-04-20 15:30:00"),
            TimePointFromString("2019-05-20 14:30:00"), TimePointFromString("2019-05-20 15:30:00"),
            TimePointFromString("2019-06-20 14:30:00"), TimePointFromString("2019-06-20 15:30:00"),
            TimePointFromString("2019-07-20 14:30:00"), TimePointFromString("2019-07-20 15:30:00"),
            TimePointFromString("2019-08-20 14:30:00"), TimePointFromString("2019-08-20 15:30:00"),
            TimePointFromString("2019-09-20 14:30:00"), TimePointFromString("2019-09-20 15:30:00"),
            TimePointFromString("2019-10-20 14:30:00"), TimePointFromString("2019-10-20 15:30:00"),
            TimePointFromString("2019-11-20 14:30:00"), TimePointFromString("2019-11-20 15:30:00"),
            TimePointFromString("2019-12-20 14:30:00"), TimePointFromString("2019-12-20 15:30:00")};

        testRow1.date_from = dates[0];
        testRow1.date_till = dates[1];
        CHECK(eventsTbl.addMonth(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("1970-10-19 10:00:00"), TimePointFromString("2040-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        uint32_t index = 0;
        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(dates[index]));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(dates[index + 1]));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            index += 2;
        }
    }

    SECTION("Check add yearly")
    {
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        REQUIRE(eventsTbl.count() == 0);

        uint32_t numberOfEvents = 4;
        std::array<TimePoint, 8> dates{TimePointFromString("2019-02-20 14:30:00"),
                                       TimePointFromString("2019-02-20 15:30:00"),
                                       TimePointFromString("2020-02-20 14:30:00"),
                                       TimePointFromString("2020-02-20 15:30:00"),
                                       TimePointFromString("2021-02-20 14:30:00"),
                                       TimePointFromString("2021-02-20 15:30:00"),
                                       TimePointFromString("2022-02-20 14:30:00"),
                                       TimePointFromString("2022-02-20 15:30:00")};

        testRow1.date_from = dates[0];
        testRow1.date_till = dates[1];
        CHECK(eventsTbl.addYear(testRow1));
        CHECK(eventsTbl.count() == numberOfEvents);

        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("1970-10-19 10:00:00"), TimePointFromString("2045-10-30 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == numberOfEvents);

        uint32_t index = 0;
        for (auto entry : entries) {
            CHECK(entry.title == testRow1.title);
            CHECK(TimePointToString(entry.date_from) == TimePointToString(dates[index]));
            CHECK(TimePointToString(entry.date_till) == TimePointToString(dates[index + 1]));
            CHECK(entry.reminder == testRow1.reminder);
            CHECK(entry.repeat == testRow1.repeat);
            CHECK(entry.reminder_fired == testRow1.reminder_fired);
            CHECK(entry.provider_type == testRow1.provider_type);
            CHECK(entry.provider_id == testRow1.provider_id);
            CHECK(entry.provider_iCalUid == testRow1.provider_iCalUid);
            CHECK(entry.isValid());
            index += 2;
        }
    }

    enum class weekDayOption
    {
        monday    = 65536,
        tuesday   = 131072,
        wednesday = 262144,
        thursday  = 524288,
        friday    = 1048576,
        saturday  = 2097152,
        sunday    = 4194304,
    };

    SECTION("Check add custom")
    {
        auto check_custom_repeat = [&](uint32_t customRepeatOption,
                                       uint32_t numberOfEvents,
                                       TimePoint originalStartDate,
                                       TimePoint originalEndDate) {
            if (eventsTbl.count() > 0) {
                REQUIRE(remove_events(eventsDb));
            }
            CHECK(eventsTbl.count() == 0);

            const uint32_t numberOfWeeks = 4;
            std::vector<bool> weekDayOptions;
            weekDayOptions = eventsTbl.parseOptions(customRepeatOption);

            testRow1.repeat    = customRepeatOption;
            testRow1.date_from = originalStartDate;
            testRow1.date_till = originalEndDate;
            CHECK(eventsTbl.addCustom(testRow1));
            CHECK(eventsTbl.count() == numberOfEvents);

            auto entries = eventsTbl.selectByDatePeriod(TimePointFromString("2018-12-10 14:30:00"),
                                                        TimePointFromString("2029-12-10 14:30:00"),
                                                        0,
                                                        numberOfEvents);
            CHECK(entries.size() == numberOfEvents);
            uint32_t orginalEventID = UINT32_MAX;
            for (auto entry : entries) {
                if (orginalEventID > entry.ID) {
                    orginalEventID = entry.ID;
                }
            }

            TimePoint expectedStartDate = TimePointFromString("2020-12-07 14:30:00"); // monday
            TimePoint expectedEndDate   = TimePointFromString("2020-12-07 15:30:00"); // monday

            uint32_t i = 0;
            for (uint32_t l = 0; l < numberOfWeeks; l++) {
                for (uint32_t j = 0; j < weekDayOptions.size(); j++) {
                    if (entries[i].ID == orginalEventID) {
                        CHECK(entries[i].title == testRow1.title);
                        CHECK(TimePointToString(entries[i].date_from) == TimePointToString(originalStartDate));
                        CHECK(TimePointToString(entries[i].date_till) == TimePointToString(originalEndDate));
                        CHECK(entries[i].reminder == testRow1.reminder);
                        CHECK(entries[i].repeat == testRow1.repeat);
                        CHECK(entries[i].reminder_fired == testRow1.reminder_fired);
                        CHECK(entries[i].provider_type == testRow1.provider_type);
                        CHECK(entries[i].provider_id == testRow1.provider_id);
                        CHECK(entries[i].provider_iCalUid == testRow1.provider_iCalUid);
                        CHECK(entries[i].isValid());
                        i++;
                    }
                    else if (weekDayOptions[j]) {
                        CHECK(entries[i].title == testRow1.title);
                        CHECK(TimePointToString(entries[i].date_from) == TimePointToString(expectedStartDate));
                        CHECK(TimePointToString(entries[i].date_till) == TimePointToString(expectedEndDate));
                        CHECK(entries[i].reminder == testRow1.reminder);
                        CHECK(entries[i].repeat == testRow1.repeat);
                        CHECK(entries[i].reminder_fired == testRow1.reminder_fired);
                        CHECK(entries[i].provider_type == testRow1.provider_type);
                        CHECK(entries[i].provider_id == testRow1.provider_id);
                        CHECK(entries[i].provider_iCalUid == testRow1.provider_iCalUid);
                        CHECK(entries[i].isValid());
                        i++;
                    }
                    expectedStartDate += 24h;
                    expectedEndDate += 24h;
                }
            }
        };

        SECTION("Repeat Mondays and Wednsdays (original thursday)")
        {
            uint32_t customRepeatOption =
                static_cast<uint32_t>(weekDayOption::monday) + static_cast<uint32_t>(weekDayOption::wednesday);
            uint32_t numberOfEvents     = 9;
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday
            LOG_DEBUG("start: %s", TimePointToString(originalStartDate + date::days{0}).c_str());
            LOG_DEBUG("start: %s", TimePointToString(originalEndDate + date::days{0}).c_str());
            LOG_DEBUG("start is: %s",
                      TimePointToString(TimePointFromTimeT(TimePointToTimeT(originalStartDate))).c_str());
            LOG_DEBUG("start is: %s", TimePointToString(TimePointFromTimeT(TimePointToTimeT(originalEndDate))).c_str());

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }

        SECTION("Repeat Mondays, Tuesdays, Wednesdays, Sundays (original thursday)")
        {
            uint32_t customRepeatOption =
                static_cast<uint32_t>(weekDayOption::monday) + static_cast<uint32_t>(weekDayOption::wednesday) +
                static_cast<uint32_t>(weekDayOption::tuesday) + static_cast<uint32_t>(weekDayOption::sunday);
            uint32_t numberOfEvents     = 17;
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }

        SECTION("Repeat Saturdays (original thursday)")
        {
            uint32_t customRepeatOption = static_cast<uint32_t>(weekDayOption::saturday);
            uint32_t numberOfEvents     = 5;
            TimePoint originalStartDate = TimePointFromString("2020-12-10 14:30:00"); // thursday
            TimePoint originalEndDate   = TimePointFromString("2020-12-10 15:30:00"); // thursday

            check_custom_repeat(customRepeatOption, numberOfEvents, originalStartDate, originalEndDate);
        }
    }

    SECTION("Check count from filter")
    {
        TimePoint from = TimePointFromString("2019-10-20 14:30:00");
        TimePoint till = TimePointFromString("2019-10-24 14:20:00");

        CHECK(eventsTbl.countFromFilter(from, till) == 4);
    }

    SECTION("Check get by UID")
    {
        /// get
        auto entry = eventsTbl.getByUID(testRow2.UID);
        check_tableRow(entry, testRow2);
    }

    SECTION("Check Add entry and Get entry by ID")
    {
        CHECK(eventsTbl.count() == 6);

        /// get
        auto entry = eventsTbl.getById(testRow2.ID);
        check_tableRow(entry, testRow2);

        auto entry2 = eventsTbl.getById(testRow4.ID);
        check_tableRow(entry2, testRow4);
    }

    SECTION("Remove entry by ID")
    {
        CHECK(eventsTbl.count() == 6);
        CHECK(eventsTbl.removeById(testRow2.ID));
        CHECK(eventsTbl.count() == 5);
        CHECK_NOTHROW(eventsTbl.getById(testRow2.ID));
    }

    SECTION("Remove entry by UID")
    {
        CHECK(eventsTbl.count() == 6);
        CHECK(eventsTbl.removeByUID(testRow2.UID));
        CHECK(eventsTbl.count() == 5);
        CHECK_NOTHROW(eventsTbl.getById(testRow2.ID));
    }

    SECTION("Update entry by ID")
    {
        CHECK(eventsTbl.count() == 6);

        std::string newTitle = "Updated Title", newProviderID = "PurePhoneUpdated";
        TimePoint newDateFrom    = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill    = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder     = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption = static_cast<uint32_t>(Repeat::biweekly);

        /// check title and provider id update
        auto entryToUpdate        = eventsTbl.getById(testRow6.ID);
        entryToUpdate.title       = newTitle;
        entryToUpdate.provider_id = newProviderID;
        CHECK(eventsTbl.update(entryToUpdate));

        auto entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);

        /// check date update
        entryToUpdate           = eventsTbl.getById(testRow5.ID);
        entryToUpdate.date_from = newDateFrom;
        entryToUpdate.date_till = newDateTill;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        CHECK(entry.ID == entryToUpdate.ID);
        CHECK(entry.UID == entryToUpdate.UID);
        CHECK(entry.title == entryToUpdate.title);
        CHECK(TimePointToString(entry.date_from) == TimePointToString(newDateFrom));
        CHECK(TimePointToString(entry.date_till) == TimePointToString(newDateTill));
        CHECK(entry.reminder == entryToUpdate.reminder);
        CHECK(entry.repeat == entryToUpdate.repeat);
        CHECK(entry.reminder_fired == entryToUpdate.reminder_fired);
        CHECK(entry.provider_type == entryToUpdate.provider_type);
        CHECK(entry.provider_id == entryToUpdate.provider_id);
        CHECK(entry.provider_iCalUid == entryToUpdate.provider_iCalUid);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check options update
        entryToUpdate          = eventsTbl.getById(testRow5.ID);
        entryToUpdate.repeat   = newRepeatOption;
        entryToUpdate.reminder = newReminder;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);
    }

    SECTION("Update entry by ID")
    {
        CHECK(eventsTbl.count() == 6);

        std::string newTitle = "Updated Title", newProviderType = "PurePhoneUpdate", newProviderID = "newID",
                    newProvideriCalUid = "new iCalUid";
        TimePoint newDateFrom          = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill          = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder           = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption       = static_cast<uint32_t>(Repeat::biweekly);

        /// check title and provider id update
        auto entryToUpdate             = eventsTbl.getById(testRow6.ID);
        entryToUpdate.title            = newTitle;
        entryToUpdate.provider_type    = newProviderType;
        entryToUpdate.provider_id      = newProviderID;
        entryToUpdate.provider_iCalUid = newProvideriCalUid;
        CHECK(eventsTbl.update(entryToUpdate));

        auto entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);

        /// check date update
        entryToUpdate           = eventsTbl.getById(testRow5.ID);
        entryToUpdate.date_from = newDateFrom;
        entryToUpdate.date_till = newDateTill;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        CHECK(entry.ID == entryToUpdate.ID);
        CHECK(entry.UID == entryToUpdate.UID);
        CHECK(entry.title == entryToUpdate.title);
        CHECK(TimePointToString(entry.date_from) == TimePointToString(newDateFrom));
        CHECK(TimePointToString(entry.date_till) == TimePointToString(newDateTill));
        CHECK(entry.reminder == entryToUpdate.reminder);
        CHECK(entry.repeat == entryToUpdate.repeat);
        CHECK(entry.reminder_fired == entryToUpdate.reminder_fired);
        CHECK(entry.provider_type == entryToUpdate.provider_type);
        CHECK(entry.provider_id == entryToUpdate.provider_id);
        CHECK(entry.provider_iCalUid == entryToUpdate.provider_iCalUid);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check options update
        entryToUpdate          = eventsTbl.getById(testRow5.ID);
        entryToUpdate.repeat   = newRepeatOption;
        entryToUpdate.reminder = newReminder;
        CHECK(eventsTbl.update(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);
    }

    SECTION("Update entry by UID")
    {
        CHECK(eventsTbl.count() == 6);

        std::string newTitle = "Updated Title", newProviderType = "PurePhoneUpdate", newProviderID = "newID",
                    newProvideriCalUid = "new iCalUid";
        TimePoint newDateFrom          = TimePointFromString("2020-10-20 15:00:00"),
                  newDateTill          = TimePointFromString("2020-10-20 16:00:00");
        uint32_t newReminder           = static_cast<uint32_t>(Reminder::one_week_before);
        uint32_t newRepeatOption       = static_cast<uint32_t>(Repeat::biweekly);

        /// check title and provider id update
        auto entryToUpdate             = eventsTbl.getByUID(testRow6.UID);
        entryToUpdate.title            = newTitle;
        entryToUpdate.provider_type    = newProviderType;
        entryToUpdate.provider_id      = newProviderID;
        entryToUpdate.provider_iCalUid = newProvideriCalUid;
        CHECK(eventsTbl.updateByUID(entryToUpdate));

        auto entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check date update
        entryToUpdate           = eventsTbl.getByUID(testRow5.UID);
        entryToUpdate.date_from = newDateFrom;
        entryToUpdate.date_till = newDateTill;
        CHECK(eventsTbl.updateByUID(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        CHECK(entry.ID == entryToUpdate.ID);
        CHECK(entry.UID == entryToUpdate.UID);
        CHECK(entry.title == entryToUpdate.title);
        CHECK(entry.date_from == newDateFrom);
        CHECK(TimePointToString(entry.date_from) == "2020-10-20 15:00:00");
        CHECK(entry.date_till == newDateTill);
        CHECK(TimePointToString(entry.date_till) == "2020-10-20 16:00:00");
        CHECK(entry.reminder == entryToUpdate.reminder);
        CHECK(entry.repeat == entryToUpdate.repeat);
        CHECK(entry.reminder_fired == entryToUpdate.reminder_fired);
        CHECK(entry.provider_type == entryToUpdate.provider_type);
        CHECK(entry.provider_id == entryToUpdate.provider_id);
        CHECK(entry.provider_iCalUid == entryToUpdate.provider_iCalUid);
        CHECK(entry.isValid());

        CHECK(eventsTbl.count() == 6);

        /// check options update
        entryToUpdate          = eventsTbl.getByUID(testRow5.UID);
        entryToUpdate.repeat   = newRepeatOption;
        entryToUpdate.reminder = newReminder;
        CHECK(eventsTbl.updateByUID(entryToUpdate));

        entry = eventsTbl.getById(entryToUpdate.ID);
        check_tableRow(entry, entryToUpdate);

        CHECK(eventsTbl.count() == 6);
    }

    SECTION("Select entry by date with limit")
    {
        auto entries = eventsTbl.selectByDatePeriod(testRow1.date_from, testRow6.date_from, 0, 3);
        REQUIRE(entries.size() == 3);
        check_tableRow(entries[0], testRow1);

        check_tableRow(entries[1], testRow2);

        check_tableRow(entries[2], testRow3);
    }

    SECTION("Select entry by date max")
    {
        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2019-10-21 10:00:00"), TimePointFromString("2019-10-24 10:00:00"), 0, UINT32_MAX);
        CHECK(entries.size() == 3);

        check_tableRow(entries[0], testRow2);

        check_tableRow(entries[1], testRow3);

        check_tableRow(entries[2], testRow4);
    }

    SECTION("Select entry by date invalid")
    {
        auto entries = eventsTbl.selectByDatePeriod(
            TimePointFromString("2000-01-01 00:00:00"), TimePointFromString("2012-12-31 23:59:00"), 0, UINT32_MAX);
        CHECK(entries.size() == 0);
    }

    SECTION("Get all limited by date")
    {
        auto entries = eventsTbl.getLimitOffsetByDate(0, 6);
        CHECK(entries.size() == 6);
    }

    SECTION("Select first upcoming event")
    {
        if (eventsTbl.count() > 0) {
            REQUIRE(remove_events(eventsDb));
        }
        CHECK(eventsTbl.count() == 0);

        TimePoint startDate1 = TimePointFromString("2018-10-20 14:24:00");
        TimePoint startDate2 = TimePointFromString("2020-10-20 14:24:00");

        TimePoint tillDate  = TimePointFromString("2030-10-20 15:24:00");
        TimePoint firedDate = TimePointFromString("2018-10-20 14:24:00");

        EventsTableRow testEvent1 = {{1},
                                     .UID              = "test1",
                                     .title            = "Event1",
                                     .date_from        = TimePointFromString("2017-10-19 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent2 = {{2},
                                     .UID              = "test2",
                                     .title            = "Event2",
                                     .date_from        = TimePointFromString("2019-10-30 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent3 = {{3},
                                     .UID              = "test3",
                                     .title            = "Event3",
                                     .date_from        = TimePointFromString("2020-10-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = firedDate,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent4 = {{4},
                                     .UID              = "test4",
                                     .title            = "Event4",
                                     .date_from        = TimePointFromString("2021-12-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = firedDate,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent5 = {{5},
                                     .UID              = "test5",
                                     .title            = "Event5",
                                     .date_from        = TimePointFromString("2022-10-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        EventsTableRow testEvent6 = {{6},
                                     .UID              = "test6",
                                     .title            = "Event6",
                                     .date_from        = TimePointFromString("2023-10-20 14:24:00"),
                                     .date_till        = tillDate,
                                     .reminder         = 120,
                                     .repeat           = 0,
                                     .reminder_fired   = TIME_POINT_INVALID,
                                     .provider_type    = "PurePhone",
                                     .provider_id      = "testID",
                                     .provider_iCalUid = "test6"};

        CHECK(eventsTbl.add(testEvent1));
        CHECK(eventsTbl.add(testEvent2));
        CHECK(eventsTbl.add(testEvent3));
        CHECK(eventsTbl.add(testEvent4));
        CHECK(eventsTbl.add(testEvent5));
        CHECK(eventsTbl.add(testEvent6));

        auto entries = eventsTbl.SelectFirstUpcoming(startDate1, tillDate);
        CHECK(entries.size() == 1);
        for (auto entry : entries) {
            CHECK(entry.title == "Event2");
        }

        entries = eventsTbl.SelectFirstUpcoming(startDate2, tillDate);
        CHECK(entries.size() == 1);
        for (auto entry : entries) {
            CHECK(entry.title == "Event5");
        }
    }

    eventsTbl.drop();
    Database::deinitialize();
}

M module-services/service-appmgr/service-appmgr/Actions.hpp => module-services/service-appmgr/service-appmgr/Actions.hpp +0 -1
@@ 44,7 44,6 @@ namespace app::manager
            ShowContactDetails,
            ShowSpecialInput,
            ShowAlarm,
            ShowReminder,
            ShowMMIResult,
            ShowMMIResponse,
            ShowMMIPush,

M module-services/service-db/ServiceDB.cpp => module-services/service-db/ServiceDB.cpp +0 -12
@@ 21,11 21,9 @@
#include <Databases/CalllogDB.hpp>
#include <Databases/ContactsDB.hpp>
#include <Databases/CountryCodesDB.hpp>
#include <Databases/EventsDB.hpp>
#include <Databases/NotesDB.hpp>
#include <Databases/NotificationsDB.hpp>
#include <Databases/SmsDB.hpp>
#include <EventsRecord.hpp>
#include <MessageType.hpp>
#include <NotesRecord.hpp>
#include <NotificationsRecord.hpp>


@@ 59,7 57,6 @@ ServiceDB::~ServiceDB()
    notesDB.reset();
    countryCodesDB.reset();
    notificationsDB.reset();
    eventsDB.reset();
    quotesDB.reset();

    Database::deinitialize();


@@ 87,8 84,6 @@ db::Interface *ServiceDB::getInterface(db::Interface::Name interface)
        return countryCodeRecordInterface.get();
    case db::Interface::Name::Notifications:
        return notificationsRecordInterface.get();
    case db::Interface::Name::Events:
        return eventsRecordInterface.get();
    case db::Interface::Name::Quotes:
        return quotesRecordInterface.get();
    }


@@ 251,7 246,6 @@ sys::ReturnCodes ServiceDB::InitHandler()
    calllogDB       = std::make_unique<CalllogDB>((purefs::dir::getUserDiskPath() / "calllog.db").c_str());
    countryCodesDB  = std::make_unique<CountryCodesDB>("country-codes.db");
    notificationsDB = std::make_unique<NotificationsDB>((purefs::dir::getUserDiskPath() / "notifications.db").c_str());
    eventsDB        = std::make_unique<EventsDB>((purefs::dir::getUserDiskPath() / "events.db").c_str());
    quotesDB        = std::make_unique<Database>((purefs::dir::getUserDiskPath() / "quotes.db").c_str());

    // Create record interfaces


@@ 265,7 259,6 @@ sys::ReturnCodes ServiceDB::InitHandler()
    countryCodeRecordInterface   = std::make_unique<CountryCodeRecordInterface>(countryCodesDB.get());
    notificationsRecordInterface =
        std::make_unique<NotificationsRecordInterface>(notificationsDB.get(), contactRecordInterface.get());
    eventsRecordInterface        = std::make_unique<EventsRecordInterface>(eventsDB.get());
    quotesRecordInterface        = std::make_unique<Quotes::QuotesAgent>(quotesDB.get());

    databaseAgents.emplace(std::make_unique<SettingsAgent>(this));


@@ 328,11 321,6 @@ bool ServiceDB::StoreIntoBackup(const std::filesystem::path &backupPath)
        return false;
    }

    if (eventsDB->storeIntoFile(backupPath / std::filesystem::path(eventsDB->getName()).filename()) == false) {
        LOG_ERROR("eventsDB backup failed");
        return false;
    }

    if (quotesDB->storeIntoFile(backupPath / std::filesystem::path(quotesDB->getName()).filename()) == false) {
        LOG_ERROR("quotesDB backup failed");
        return false;

M module-services/service-db/ServiceDB.hpp => module-services/service-db/ServiceDB.hpp +0 -5
@@ 17,7 17,6 @@
#include <Interface/CalllogRecord.hpp>
#include <Interface/ContactRecord.hpp>
#include <Interface/CountryCodeRecord.hpp>
#include <Interface/EventsRecord.hpp>
#include <Interface/NotesRecord.hpp>
#include <Interface/SMSRecord.hpp>
#include <Interface/SMSTemplateRecord.hpp>


@@ 39,8 38,6 @@ class ContactsDB;
class CountryCodeRecordInterface;
class CountryCodesDB;
class DatabaseAgent;
class EventsDB;
class EventsRecordInterface;
class NotesDB;
class NotesRecordInterface;
class NotificationsDB;


@@ 65,7 62,6 @@ class ServiceDB : public sys::Service
    std::unique_ptr<CalllogDB> calllogDB;
    std::unique_ptr<CountryCodesDB> countryCodesDB;
    std::unique_ptr<NotificationsDB> notificationsDB;
    std::unique_ptr<EventsDB> eventsDB;
    std::unique_ptr<Database> quotesDB;

    std::unique_ptr<SMSRecordInterface> smsRecordInterface;


@@ 77,7 73,6 @@ class ServiceDB : public sys::Service
    std::unique_ptr<CalllogRecordInterface> calllogRecordInterface;
    std::unique_ptr<CountryCodeRecordInterface> countryCodeRecordInterface;
    std::unique_ptr<NotificationsRecordInterface> notificationsRecordInterface;
    std::unique_ptr<EventsRecordInterface> eventsRecordInterface;
    std::unique_ptr<Quotes::QuotesAgent> quotesRecordInterface;

  protected:

M module-services/service-desktop/CMakeLists.txt => module-services/service-desktop/CMakeLists.txt +0 -2
@@ 29,8 29,6 @@ set(SOURCES
    endpoints/filesystem/FilesystemEndpoint.cpp
    endpoints/filesystem/FileOperations.cpp
    endpoints/filesystem/FileContext.cpp
    endpoints/calendarEvents/CalendarEventsHelper.cpp
    endpoints/calendarEvents/CalendarEventsEndpoint.cpp
    endpoints/security/SecurityEndpoint.cpp
    endpoints/security/SecurityEndpointHelper.cpp


M module-services/service-desktop/endpoints/Context.hpp => module-services/service-desktop/endpoints/Context.hpp +0 -3
@@ 187,7 187,6 @@ namespace parserFSM
    namespace endpoint_pageing
    {
        inline constexpr std::size_t contactsPageSize       = 10;
        inline constexpr std::size_t calendarEventsPageSize = 10;
        inline constexpr std::size_t messagesPageSize       = 4;
    } // namespace endpoint_pageing



@@ 198,8 197,6 @@ namespace parserFSM
        {
            switch (static_cast<EndpointType>(js[json::endpoint].int_value())) {
            // enable for pagination in other endpoints
            case EndpointType::calendarEvents:
                return std::make_unique<PagedContext>(js, endpoint_pageing::calendarEventsPageSize);
            // case EndpointType::calllog:
            case EndpointType::contacts:
                return std::make_unique<PagedContext>(js, endpoint_pageing::contactsPageSize);

M module-services/service-desktop/endpoints/EndpointFactory.hpp => module-services/service-desktop/endpoints/EndpointFactory.hpp +0 -3
@@ 6,7 6,6 @@
#include "Endpoint.hpp"

#include "Service/Service.hpp"
#include "calendarEvents/CalendarEventsEndpoint.hpp"
#include "backup/BackupEndpoint.hpp"
#include "deviceInfo/DeviceInfoEndpoint.hpp"
#include "update/UpdateEndpoint.hpp"


@@ 50,8 49,6 @@ class EndpointFactory
            return std::make_unique<CalllogEndpoint>(ownerServicePtr);
        case parserFSM::EndpointType::developerMode:
            return std::make_unique<DeveloperModeEndpoint>(ownerServicePtr);
        case parserFSM::EndpointType::calendarEvents:
            return std::make_unique<CalendarEventsEndpoint>(ownerServicePtr);
        case parserFSM::EndpointType::bluetooth:
            return std::make_unique<BluetoothEndpoint>(ownerServicePtr);
        case parserFSM::EndpointType::usbSecurity:

D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsEndpoint.cpp => module-services/service-desktop/endpoints/calendarEvents/CalendarEventsEndpoint.cpp +0 -27
@@ 1,27 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "CalendarEventsEndpoint.hpp"
#include "queries/phonebook/QueryContactGet.hpp"
#include <memory>
#include <string>

using namespace parserFSM;

auto CalendarEventsEndpoint::handle(Context &context) -> void
{
    switch (context.getMethod()) {
    case http::Method::get:
        helper->requestDataFromDB(context);
        break;
    case http::Method::post: // update entry
        helper->updateDBEntry(context);
        break;
    case http::Method::put:
        helper->createDBEntry(context);
        break;
    case http::Method::del:
        helper->deleteDBEntry(context);
        break;
    }
}

D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsEndpoint.hpp => module-services/service-desktop/endpoints/calendarEvents/CalendarEventsEndpoint.hpp +0 -22
@@ 1,22 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <endpoints/Endpoint.hpp>
#include "Service/Service.hpp"
#include "CalendarEventsHelper.hpp"

class CalendarEventsEndpoint : public parserFSM::Endpoint
{
  private:
    std::string debugName = "CalendarEventsEndpoint";
    std::unique_ptr<parserFSM::CalendarEventsHelper> helper;

  public:
    explicit CalendarEventsEndpoint(sys::Service *_ownerServicePtr) : Endpoint(_ownerServicePtr)
    {
        helper = std::make_unique<parserFSM::CalendarEventsHelper>(ownerServicePtr);
    }
    void handle(parserFSM::Context &context) override;
};

D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp => module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.cpp +0 -486
@@ 1,486 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "EventsRecord.hpp"
#include "CalendarEventsHelper.hpp"
#include "Common/Query.hpp"
#include <parser/ParserUtils.hpp>
#include "Service/Common.hpp"
#include <service-db/DBServiceAPI.hpp>
#include <log.hpp>
#include <queries/calendar/QueryEventsGetAll.hpp>
#include <queries/calendar/QueryEventsGet.hpp>
#include <queries/calendar/QueryEventsAdd.hpp>
#include <queries/calendar/QueryEventsEdit.hpp>
#include <queries/calendar/QueryEventsEditICS.hpp>
#include <queries/calendar/QueryEventsRemove.hpp>
#include <queries/calendar/QueryEventsRemoveICS.hpp>
#include <queries/calendar/QueryEventsGetAllLimited.hpp>
#include <variant>
#include <utility>

namespace parserFSM
{
    namespace ical
    {
        namespace duration
        {
            const inline auto five_minutes_before    = Duration(0, 0, 0, 5);
            const inline auto fifteen_minutes_before = Duration(0, 0, 0, 15);
            const inline auto thirty_minutes_before  = Duration(0, 0, 0, 30);
            const inline auto one_hour_before        = Duration(0, 0, 1, 0);
            const inline auto two_hours_before       = Duration(0, 0, 2, 0);
            const inline auto one_day_before         = Duration(0, 1, 0, 0);
            const inline auto two_days_before        = Duration(0, 2, 0, 0);
            const inline auto one_week_before        = Duration(1, 0, 0, 0);
            const inline auto event_time             = Duration(0, 0, 0, 0);
            const inline auto never                  = Duration(0, 0, 0, 0xFFFF);
        } // namespace duration
    }     // namespace ical

    namespace json::calendar
    {
        constexpr inline auto events = "calendar_events";
        namespace event
        {
            constexpr inline auto vevent = "VEVENT";

            constexpr inline auto uid     = "UID";
            constexpr inline auto summary = "SUMMARY";
            constexpr inline auto dtstart = "DTSTART";
            constexpr inline auto dtend   = "DTEND";

            namespace recurrence_rule
            {
                constexpr inline auto rrule = "RRULE";

                constexpr inline auto frequency = "FREQ";
                constexpr inline auto count     = "COUNT";
                constexpr inline auto interval  = "INTERVAL";
            } // namespace recurrence_rule

            namespace alarm
            {
                constexpr inline auto valarm = "VALARM";

                constexpr inline auto trigger = "TRIGGER";
                constexpr inline auto action  = "ACTION";
            } // namespace alarm

            namespace provider
            {
                constexpr inline auto provider = "provider";

                constexpr inline auto type    = "type";
                constexpr inline auto id      = "id";
                constexpr inline auto iCalUid = "iCalUid";
            } // namespace provider
        }     // namespace event
        constexpr inline auto limit  = "limit";
        constexpr inline auto offset = "offset";

    } // namespace json::calendar
} // namespace parserFSM
using namespace parserFSM;

auto CalendarEventsHelper::isICalEventValid(const ICalEvent &icalEvent) const -> bool
{
    if (!icalEvent.event.isValid) {
        LOG_ERROR("Ical event invalid!");
        return false;
    }
    if (!icalEvent.alarm.isValid) {
        LOG_ERROR("Ical alarm invalid!");
        return false;
    }
    if (!icalEvent.rrule.isValid) {
        LOG_ERROR("Ical recurrence rule invalid!");
        return false;
    }
    return true;
}

auto CalendarEventsHelper::frequencyFromCustomRepeat(Repeat repeat) const -> Frequency
{
    return Frequency(static_cast<uint32_t>(repeat));
}

auto CalendarEventsHelper::recurrenceRuleFrom(Repeat repeat) const -> RecurrenceRule
{
    if (repeat > Repeat::yearly) {
        uint32_t count = 1, interval = 1;
        return RecurrenceRule(frequencyFromCustomRepeat(repeat), count, interval);
    }
    switch (repeat) {
    case Repeat::never: {
        return RecurrenceRule();
    }
    case Repeat::daily: {
        uint32_t count = 7, interval = 1;
        return RecurrenceRule(Frequency::daily, count, interval);
    }
    case Repeat::weekly: {
        uint32_t count = 4, interval = 1;
        return RecurrenceRule(Frequency::weekly, count, interval);
    }
    case Repeat::biweekly: {
        uint32_t count = 4, interval = 2;
        return RecurrenceRule(Frequency::weekly, count, interval);
    }
    case Repeat::monthly: {
        uint32_t count = 12, interval = 1;
        return RecurrenceRule(Frequency::monthly, count, interval);
    }
    case Repeat::yearly: {
        uint32_t count = 4, interval = 1;
        return RecurrenceRule(Frequency::yearly, count, interval);
    }
    }

    return RecurrenceRule();
}

auto CalendarEventsHelper::alarmFrom(Reminder reminder) const -> Alarm
{
    switch (reminder) {
    case Reminder::never: {
        auto beforeEvent = ical::duration::never;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::five_min_before: {
        auto beforeEvent = ical::duration::five_minutes_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::fifteen_min_before: {
        auto beforeEvent = ical::duration::fifteen_minutes_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::thirty_min_before: {
        auto beforeEvent = ical::duration::thirty_minutes_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::one_hour_before: {
        auto beforeEvent = ical::duration::one_hour_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::two_hour_before: {
        auto beforeEvent = ical::duration::two_hours_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::one_day_before: {
        auto beforeEvent = ical::duration::one_day_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::two_days_before: {
        auto beforeEvent = ical::duration::two_days_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::one_week_before: {
        auto beforeEvent = ical::duration::one_week_before;
        return Alarm(beforeEvent, Action::none);
    }
    case Reminder::event_time: {
        auto beforeEvent = ical::duration::event_time;
        return Alarm(beforeEvent, Action::none);
    }
    }
    return Alarm();
}

auto CalendarEventsHelper::icalEventFrom(const EventsRecord &record) const -> ICalEvent
{

    auto event = Event(record.title, record.date_from, record.date_till, record.UID);
    auto alarm = alarmFrom(Reminder(record.reminder));
    auto rrule = recurrenceRuleFrom(Repeat(record.repeat));
    return ICalEvent{event, alarm, rrule};
}

auto CalendarEventsHelper::eventJsonObjectFrom(const EventsRecord &record) const -> json11::Json
{
    auto icalEvent = icalEventFrom(record);
    if (!isICalEventValid(icalEvent)) {
        LOG_ERROR("Bad event record formatting");
    }

    auto rruleObj = json11::Json::object{
        {json::calendar::event::recurrence_rule::frequency, icalEvent.rrule.getFrequencyString().c_str()},
        {json::calendar::event::recurrence_rule::count, icalEvent.rrule.getCountString().c_str()},
        {json::calendar::event::recurrence_rule::interval, icalEvent.rrule.getIntervalString().c_str()}};

    auto alarmObj =
        json11::Json::object{{json::calendar::event::alarm::trigger, icalEvent.alarm.getTriggerString().c_str()},
                             {json::calendar::event::alarm::action, icalEvent.alarm.getActionString().c_str()}};

    auto providerObj =
        json11::Json::object{{json::calendar::event::provider::type, record.provider_type.c_str()},
                             {json::calendar::event::provider::id, record.provider_id.c_str()},
                             {json::calendar::event::provider::iCalUid, record.provider_iCalUid.c_str()}};

    auto eventObj = json11::Json::object{{json::calendar::event::dtend, icalEvent.event.getDTEndString().c_str()},
                                         {json::calendar::event::dtstart, icalEvent.event.getDTStartString().c_str()},
                                         {json::calendar::event::summary, icalEvent.event.getSummary().c_str()},
                                         {json::calendar::event::uid, icalEvent.event.getUID().c_str()},
                                         {json::calendar::event::recurrence_rule::rrule, rruleObj},
                                         {json::calendar::event::alarm::valarm, alarmObj},
                                         {json::calendar::event::provider::provider, providerObj}};

    return eventObj;
}

auto CalendarEventsHelper::requestDataFromDB(Context &context) -> sys::ReturnCodes
{
    try {
        auto &ctx          = dynamic_cast<PagedContext &>(context);
        std::size_t limit  = ctx.getBody()[json::calendar::limit].int_value();
        std::size_t offset = ctx.getBody()[json::calendar::offset].int_value();
        ctx.setRequestedLimit(limit);
        ctx.setRequestedOffset(offset);
        auto query = std::make_unique<db::query::events::GetAllLimited>(offset, std::min(ctx.getPageSize(), limit));

        auto listener = std::make_unique<db::EndpointListenerWithPages>(
            [&](db::QueryResult *result, PagedContext &context) {
                if (auto EventsResult = dynamic_cast<db::query::events::GetAllLimitedResult *>(result)) {
                    auto records        = EventsResult->getResult();
                    uint32_t totalCount = EventsResult->getCountResult();
                    auto parser         = std::make_unique<ParserICS>();
                    std::vector<ICalEvent> icalEvents;
                    context.setTotalCount(totalCount);
                    auto eventsArray = json11::Json::array();

                    for (const auto &rec : records) {
                        auto eventObject = eventJsonObjectFrom(rec);
                        eventsArray.emplace_back(eventObject);
                    }
                    context.setResponseBody(eventsArray);
                    MessageHandler::putToSendQueue(context.createSimpleResponse(json::calendar::events));

                    return true;
                }
                return false;
            },
            ctx);

        query->setQueryListener(std::move(listener));
        DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
    }
    catch (const std::exception &e) {
        LOG_ERROR("%s", e.what());
        return sys::ReturnCodes::Failure;
    }
    return sys::ReturnCodes::Success;
}

auto CalendarEventsHelper::frequencyToCustomRepeat(Frequency freq) const -> uint32_t
{
    return static_cast<uint32_t>(freq);
}

auto CalendarEventsHelper::repeatFrom(RecurrenceRule &rrule) const -> Repeat
{
    auto freq = rrule.getFrequencyValue();
    switch (freq) {
    case Frequency::daily: {
        return Repeat::daily;
    }
    case Frequency::weekly: {
        if (rrule.getIntervalValue() == 1) {
            return Repeat::weekly;
        }
        else if (rrule.getIntervalValue() == 2) {
            return Repeat::biweekly;
        }
        else {
            LOG_ERROR("Interval invalid");
            return Repeat::never;
        }
    }
    case Frequency::monthly: {
        return Repeat::monthly;
    }
    case Frequency::yearly: {
        return Repeat::yearly;
    }
    case Frequency::never: {
        return Repeat::never;
    }
    }
    return Repeat::never;
}

auto CalendarEventsHelper::eventsRecordFrom(ICalEvent &icalEvent) const -> EventsRecord
{

    auto record      = EventsRecord();
    record.UID       = icalEvent.event.getUID();
    record.title     = icalEvent.event.getSummary();
    record.date_from = icalEvent.event.getDTStartTimePoint();
    record.date_till = icalEvent.event.getDTEndTimePoint();
    if (icalEvent.rrule.getFrequencyValue() > Frequency::yearly) {
        record.repeat = frequencyToCustomRepeat(icalEvent.rrule.getFrequencyValue());
    }
    record.repeat   = static_cast<uint32_t>(repeatFrom(icalEvent.rrule));
    record.reminder = icalEvent.alarm.getTriggerValue().getDurationInMinutes();

    return record;
}

auto CalendarEventsHelper::ICalEventFromJson(const json11::Json &eventObj) const -> ICalEvent
{
    ICalEvent icalEvent;
    icalEvent.event.setUID(eventObj[json::calendar::event::uid].string_value());
    icalEvent.event.setSummary(eventObj[json::calendar::event::summary].string_value());
    icalEvent.event.setDTStart(eventObj[json::calendar::event::dtstart].string_value());
    icalEvent.event.setDTEnd(eventObj[json::calendar::event::dtend].string_value());

    icalEvent.rrule.setFrequency(
        eventObj[json::calendar::event::recurrence_rule::rrule][json::calendar::event::recurrence_rule::frequency]
            .string_value());
    icalEvent.rrule.setCount(
        eventObj[json::calendar::event::recurrence_rule::rrule][json::calendar::event::recurrence_rule::count]
            .string_value());
    icalEvent.rrule.setInterval(
        eventObj[json::calendar::event::recurrence_rule::rrule][json::calendar::event::recurrence_rule::interval]
            .string_value());

    icalEvent.alarm.setTrigger(
        eventObj[json::calendar::event::alarm::valarm][json::calendar::event::alarm::trigger].string_value());
    icalEvent.alarm.setAction(
        eventObj[json::calendar::event::alarm::valarm][json::calendar::event::alarm::action].string_value());

    auto record = eventsRecordFrom(icalEvent);

    return icalEvent;
}

auto CalendarEventsHelper::createDBEntry(Context &context) -> sys::ReturnCodes
{
    const auto eventsJsonObj   = context.getBody();
    const auto eventsJsonArray = eventsJsonObj[json::calendar::events].array_items();
    bool ret                   = true;
    for (const auto &event : eventsJsonArray) {

        auto icalEvent = ICalEventFromJson(event);

        if (!isICalEventValid(icalEvent)) {
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Failure;
        }

        auto record = eventsRecordFrom(icalEvent);
        if (record.UID.empty()) {
            record.UID   = createUID();
            auto jsonObj = json11::Json::object({{json::calendar::event::uid, record.UID}});
            context.setResponseBody(jsonObj);
        }
        else {
            LOG_ERROR("UID should not be recieved in put event endpoint.");
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Failure;
        }

        auto query    = std::make_unique<db::query::events::Add>(record);
        auto listener = std::make_unique<db::EndpointListener>(
            [&](db::QueryResult *result, Context context) {
                if (auto EventResult = dynamic_cast<db::query::events::AddResult *>(result)) {

                    context.setResponseStatus(EventResult->getResult() ? http::Code::OK
                                                                       : http::Code::InternalServerError);

                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                return false;
            },
            context);

        query->setQueryListener(std::move(listener));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
        ret = ret && succeed;
    }

    if (ret) {
        return sys::ReturnCodes::Success;
    }
    else {
        return sys::ReturnCodes::Failure;
    }
}

auto CalendarEventsHelper::updateDBEntry(Context &context) -> sys::ReturnCodes
{
    auto eventsJsonObj = context.getBody();

    bool ret = true;
    for (const auto &event : eventsJsonObj[json::calendar::events].array_items()) {

        auto icalEvent = ICalEventFromJson(event);
        if (!isICalEventValid(icalEvent) || icalEvent.event.getUID().empty()) {
            context.setResponseStatus(http::Code::BadRequest);
            MessageHandler::putToSendQueue(context.createSimpleResponse());
            return sys::ReturnCodes::Failure;
        }

        auto record   = eventsRecordFrom(icalEvent);
        auto query    = std::make_unique<db::query::events::EditICS>(record);
        auto listener = std::make_unique<db::EndpointListener>(
            [](db::QueryResult *result, Context context) {
                if (auto EventResult = dynamic_cast<db::query::events::EditICSResult *>(result)) {
                    context.setResponseStatus(EventResult->getResult() ? http::Code::NoContent
                                                                       : http::Code::InternalServerError);
                    MessageHandler::putToSendQueue(context.createSimpleResponse());
                    return true;
                }
                return false;
            },
            context);

        query->setQueryListener(std::move(listener));
        const auto [succeed, _] =
            DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));
        ret = ret && succeed;
    }
    if (ret) {
        return sys::ReturnCodes::Success;
    }
    else {
        return sys::ReturnCodes::Failure;
    }
}

auto CalendarEventsHelper::deleteDBEntry(Context &context) -> sys::ReturnCodes
{
    auto UID      = context.getBody()[json::calendar::event::uid].string_value();
    auto checkUID = Event();
    checkUID.setUID(UID);
    if (!checkUID.isValid) {
        LOG_ERROR("Wrong UID format.");
        context.setResponseStatus(http::Code::BadRequest);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
        return sys::ReturnCodes::Failure;
    }
    auto query    = std::make_unique<db::query::events::RemoveICS>(UID);
    auto listener = std::make_unique<db::EndpointListener>(
        [=](db::QueryResult *result, Context context) {
            if (auto EventResult = dynamic_cast<db::query::events::RemoveICSResult *>(result)) {
                context.setResponseStatus(EventResult->getResult() ? http::Code::OK : http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                return true;
            }
            return false;
        },
        context);

    query->setQueryListener(std::move(listener));
    auto [ret, _] = DBServiceAPI::GetQuery(ownerServicePtr, db::Interface::Name::Events, std::move(query));

    if (ret) {
        return sys::ReturnCodes::Success;
    }
    else {
        return sys::ReturnCodes::Failure;
    }
}

D module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.hpp => module-services/service-desktop/endpoints/calendarEvents/CalendarEventsHelper.hpp +0 -45
@@ 1,45 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include "Common/Query.hpp"
#include "EventsRecord.hpp"
#include "Service/Service.hpp"
#include "Service/Common.hpp"
#include <memory>
#include <service-db/DBServiceAPI.hpp>
#include <endpoints/Endpoint.hpp>
#include <endpoints/Context.hpp>
#include <endpoints/DBHelper.hpp>

#include <module-utils/ical/ParserICS.hpp>

namespace parserFSM
{

    class CalendarEventsHelper : public DBHelper
    {
        [[nodiscard]] auto frequencyFromCustomRepeat(Repeat repeat) const -> Frequency;
        [[nodiscard]] auto recurrenceRuleFrom(Repeat repeat) const -> RecurrenceRule;
        [[nodiscard]] auto alarmFrom(Reminder reminder) const -> Alarm;
        [[nodiscard]] auto icalEventFrom(const EventsRecord &record) const -> ICalEvent;

        [[nodiscard]] auto frequencyToCustomRepeat(Frequency freq) const -> uint32_t;
        [[nodiscard]] auto repeatFrom(RecurrenceRule &rrule) const -> Repeat;
        [[nodiscard]] auto eventsRecordFrom(ICalEvent &icalEvent) const -> EventsRecord;

        [[nodiscard]] auto eventJsonObjectFrom(const EventsRecord &record) const -> json11::Json;
        [[nodiscard]] auto ICalEventFromJson(const json11::Json &eventObj) const -> ICalEvent;
        [[nodiscard]] auto isICalEventValid(const ICalEvent &event) const -> bool;

      public:
        explicit CalendarEventsHelper(sys::Service *_ownerServicePtr) : DBHelper(_ownerServicePtr)
        {}

        auto createDBEntry(Context &context) -> sys::ReturnCodes override;
        auto requestDataFromDB(Context &context) -> sys::ReturnCodes override;
        auto updateDBEntry(Context &context) -> sys::ReturnCodes override;
        auto deleteDBEntry(Context &context) -> sys::ReturnCodes override;
    };
} // namespace parserFSM

M module-services/service-desktop/parser/ParserUtils.hpp => module-services/service-desktop/parser/ParserUtils.hpp +0 -1
@@ 25,7 25,6 @@ namespace parserFSM
        contacts,
        messages,
        calllog,
        calendarEvents,
        developerMode,
        bluetooth,
        usbSecurity

M module-services/service-time/CMakeLists.txt => module-services/service-time/CMakeLists.txt +0 -2
@@ 6,8 6,6 @@ set(SOURCES
    TimeManager.cpp
    RTCcommand.cpp
    TimezoneHandler.cpp
    timeEvents/TimeEvents.cpp
    timeEvents/CalendarTimeEvents.cpp
    internal/StaticData.cpp
)


M module-services/service-time/ServiceTime.cpp => module-services/service-time/ServiceTime.cpp +2 -62
@@ 2,7 2,6 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ServiceTime.hpp"
#include "service-time/CalendarTimeEvents.hpp"
#include "service-time/TimeMessage.hpp"
#include "service-time/RTCCommand.hpp"
#include <service-time/internal/StaticData.hpp>


@@ 13,7 12,6 @@
#include <Common/Query.hpp>
#include <MessageType.hpp>
#include <log.hpp>
#include <module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp>
#include <service-db/DBNotificationMessage.hpp>
#include <service-db/QueryMessage.hpp>
#include <service-cellular/service-cellular/CellularMessage.hpp>


@@ 34,7 32,7 @@ namespace stm
    constexpr auto automaticTimezoneName = "";
    constexpr auto automaticTimezoneRules = "UTC0";

    ServiceTime::ServiceTime() : sys::Service(service::name::service_time, "", StackDepth), calendarEvents(this)
    ServiceTime::ServiceTime() : sys::Service(service::name::service_time, "", StackDepth)
    {
        LOG_INFO("[ServiceTime] Initializing");
        bus.channels.push_back(sys::BusChannel::ServiceDBNotifications);


@@ 79,66 77,8 @@ namespace stm

    sys::MessagePointer ServiceTime::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp)
    {
        std::shared_ptr<sys::ResponseMessage> responseMsg = nullptr;

        switch (static_cast<MessageType>(msgl->messageType)) {
        case MessageType::DBServiceNotification: {
            auto msg = dynamic_cast<db::NotificationMessage *>(msgl);
            if (msg == nullptr) {
                responseMsg = std::make_shared<TimeResponseMessage>(false);
                break;
            }
            if (msg->interface == db::Interface::Name::Events && msg->dataModified()) {

                calendarEvents.processNextEvent();

                return responseMsg;
            }
        } break;
        case MessageType::ReloadTimers: {
            calendarEvents.processNextEvent();
            return std::make_shared<sys::ResponseMessage>();
        } break;
        case MessageType::TimersProcessingStart: {
            calendarEvents.startProcessing();
            return std::make_shared<sys::ResponseMessage>();
        } break;
        case MessageType::TimersProcessingStop: {
            calendarEvents.stopProcessing();
            return std::make_shared<sys::ResponseMessage>();
        } break;
        default:
            break;
        }

        if (responseMsg != nullptr) {
            responseMsg->responseTo = msgl->messageType;
            return responseMsg;
        }

        bool responseHandled = false;
        if (resp != nullptr) {
            if (auto msg = dynamic_cast<db::QueryResponse *>(resp)) {
                auto result = msg->getResult();

                if (dynamic_cast<db::query::events::SelectFirstUpcomingResult *>(result.get())) {

                    calendarEvents.receiveNextEventQuery(std::move(result));
                    responseHandled = true;
                }
            }
            if (responseHandled) {
                return std::make_shared<sys::ResponseMessage>();
            }
            else {
                return std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Unresolved);
            }
        }
        else {
            return std::make_shared<sys::ResponseMessage>();
        }
        return std::make_shared<sys::ResponseMessage>();
    }

    void ServiceTime::registerMessageHandlers()
    {
        connect(typeid(CellularTimeNotificationMessage), [&](sys::Message *request) -> sys::MessagePointer {

M module-services/service-time/ServiceTime.hpp => module-services/service-time/ServiceTime.hpp +0 -4
@@ 4,7 4,6 @@
#pragma once

#include "Constants.hpp"
#include "service-time/CalendarTimeEvents.hpp"
#include "service-time/TimeManager.hpp"
#include "service-time/ServiceTime.hpp"



@@ 14,8 13,6 @@
#include <Service/Common.hpp>
#include <Service/Message.hpp>
#include <Service/Service.hpp>
#include <module-db/queries/calendar/QueryEventsEdit.hpp>
#include <module-db/queries/calendar/QueryEventsGet.hpp>

#include <functional>
#include <string> // for allocator, string


@@ 31,7 28,6 @@ namespace stm
    {
      private:
        static constexpr auto StackDepth = 2048;
        CalendarTimeEvents calendarEvents;

        std::unique_ptr<TimeManager> timeManager;


D module-services/service-time/service-time/CalendarTimeEvents.hpp => module-services/service-time/service-time/CalendarTimeEvents.hpp +0 -49
@@ 1,49 0,0 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once
#include "TimeEvents.hpp"

#include <application-calendar/data/dateCommon.hpp>
#include <module-db/Interface/EventsRecord.hpp>
#include <module-db/queries/calendar/QueryEventsEdit.hpp>

#include <cstdint>
#include <memory>
#include <string>

namespace db
{
    class QueryResult;
} // namespace db
namespace sys
{
    class Service;
} // namespace sys

namespace stm
{
    class CalendarTimeEvents : public TimeEvents
    {
      private:
        EventsRecord eventRecord;
        TimePoint startTP = TIME_POINT_INVALID;

      protected:
        const std::string timerName() override
        {
            return "CalendarTimeEvents_timer";
        };
        uint32_t calcToNextEventInterval(std::unique_ptr<db::QueryResult> nextEventQueryResult) override;

        bool sendNextEventQuery() override;
        bool sendEventFiredQuery() override;
        void invokeEvent() override;

      public:
        CalendarTimeEvents() = delete;
        explicit CalendarTimeEvents(sys::Service *service);
        ~CalendarTimeEvents() = default;
    };

} // namespace stm

D module-services/service-time/service-time/TimeEvents.hpp => module-services/service-time/service-time/TimeEvents.hpp +0 -67
@@ 1,67 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <Service/Service.hpp>
#include <module-db/queries/calendar/QueryEventsEdit.hpp>
#include <module-sys/Timers/TimerHandle.hpp>

#include <cstdint>
#include <memory>
#include <string>

namespace db
{
    class QueryResult;
} // namespace db
namespace sys
{
    class Service;
} // namespace sys

namespace stm
{
    class TimeEvents
    {
      private:
        bool timersProcessingStarted = false;
        sys::TimerHandle fireEventTimer;
        sys::Service *serv = nullptr;
        sys::TimerHandle &timer();

      protected:
        sys::Service *service()
        {
            return serv;
        };
        virtual const std::string timerName() = 0;
        void stopTimer();
        void recreateTimer(uint32_t interval);
        virtual void fireEventTimerCallback();
        virtual uint32_t calcToNextEventInterval(std::unique_ptr<db::QueryResult> nextEventQueryResult) = 0;

        virtual bool sendNextEventQuery()  = 0;
        virtual bool sendEventFiredQuery() = 0;
        virtual void invokeEvent(){};

      public:
        TimeEvents() = delete;
        explicit TimeEvents(sys::Service *service);
        ~TimeEvents();

        /// startProcessing - starts processing of events
        void startProcessing();
        /// stopProcessing - stops processing of events
        void stopProcessing();
        /// isStarted - returns events' processing state
        bool isStarted()
        {
            return timersProcessingStarted;
        };
        /// processNextEvent - starts next event query
        void processNextEvent();
        /// receiveNextEventQuery - receives result of next event query
        virtual bool receiveNextEventQuery(std::unique_ptr<db::QueryResult> nextEventQueryResult);
    };
} // namespace stm

M module-services/service-time/service-time/TimeMessage.hpp => module-services/service-time/service-time/TimeMessage.hpp +0 -21
@@ 15,27 15,6 @@ class TimeMessage : public sys::DataMessage
    virtual ~TimeMessage() = default;
};

class TimersProcessingStartMessage : public TimeMessage
{
  public:
    TimersProcessingStartMessage() : TimeMessage(MessageType::TimersProcessingStart)
    {}
};

class TimersProcessingStopMessage : public TimeMessage
{
  public:
    TimersProcessingStopMessage() : TimeMessage(MessageType::TimersProcessingStop)
    {}
};

class ReloadTimersMessage : public TimeMessage
{
  public:
    ReloadTimersMessage() : TimeMessage(MessageType::ReloadTimers)
    {}
};

class TimeResponseMessage : public sys::ResponseMessage
{
  public:

M module-services/service-time/service-time/TimeSettings.hpp => module-services/service-time/service-time/TimeSettings.hpp +1 -0
@@ 4,6 4,7 @@
#pragma once

#include <time/time_conversion.hpp>
#include <service-time/api/TimeSettingsApi.hpp>

class TimeSettings : public utils::time::TimeSettingsInterface
{

D module-services/service-time/timeEvents/CalendarTimeEvents.cpp => module-services/service-time/timeEvents/CalendarTimeEvents.cpp +0 -93
@@ 1,93 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-time/CalendarTimeEvents.hpp>
#include <service-time/TimeEvents.hpp>

#include <BaseInterface.hpp>
#include <Common/Query.hpp>
#include <application-calendar/widgets/CalendarStyle.hpp>
#include <module-apps/application-calendar/ApplicationCalendar.hpp>
#include <module-apps/application-calendar/data/CalendarData.hpp>
#include <module-apps/application-calendar/data/dateCommon.hpp>
#include <module-db/queries/calendar/QueryEventsSelectFirstUpcoming.hpp>
#include <module-gui/gui/SwitchData.hpp>
#include <queries/calendar/QueryEventsEdit.hpp>
#include <service-appmgr/Controller.hpp>
#include <service-db/DBServiceAPI.hpp>

#include <algorithm>
#include <chrono>
#include <type_traits>
#include <utility>
#include <vector>

namespace sys
{
    class Service;
} // namespace sys

namespace stm
{
    using namespace std::chrono_literals;
    constexpr static auto eventTimerMinSkipInterval = 100ms;

    CalendarTimeEvents::CalendarTimeEvents(sys::Service *service) : TimeEvents(service)
    {}

    bool CalendarTimeEvents::sendNextEventQuery()
    {
        TimePoint filterFrom = TimePointNow();
        TimePoint filterTill = filterFrom;
        if (startTP != TIME_POINT_INVALID) {
            filterFrom = std::min(startTP, filterFrom);
            filterTill = filterFrom;
        }

        const auto [succeed, _] =
            DBServiceAPI::GetQuery(service(),
                                   db::Interface::Name::Events,
                                   std::make_unique<db::query::events::SelectFirstUpcoming>(filterFrom, filterTill));
        return succeed;
    }

    uint32_t CalendarTimeEvents::calcToNextEventInterval(std::unique_ptr<db::QueryResult> nextEventQueryResult)
    {
        auto firstUpcomingQuery =
            dynamic_cast<db::query::events::SelectFirstUpcomingResult *>(nextEventQueryResult.get());
        if (firstUpcomingQuery == nullptr) {
            return 0;
        }

        std::vector<EventsRecord> records = firstUpcomingQuery->getResult();
        if (records.size() == 0) {
            return 0;
        }

        eventRecord   = records.at(0);
        startTP       = eventRecord.date_from - std::chrono::minutes{eventRecord.reminder};
        auto duration = eventRecord.date_from - std::chrono::minutes{eventRecord.reminder} - TimePointNow();
        if (duration.count() <= 0) {
            duration = std::chrono::milliseconds(eventTimerMinSkipInterval);
        }

        return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
    }

    bool CalendarTimeEvents::sendEventFiredQuery()
    {
        eventRecord.reminder_fired = TimePointNow();
        const auto [succeed, _]    = DBServiceAPI::GetQuery(
            service(), db::Interface::Name::Events, std::make_unique<db::query::events::Edit>(eventRecord));
        return succeed;
    }

    void CalendarTimeEvents::invokeEvent()
    {
        auto event = std::make_shared<EventsRecord>(eventRecord);
        auto eventData = std::make_unique<EventRecordData>(std::move(event));
        eventData->setDescription(style::window::calendar::name::event_reminder_window);

        app::manager::Controller::sendAction(service(), app::manager::actions::ShowReminder, std::move(eventData));
    }
} // namespace stm

D module-services/service-time/timeEvents/TimeEvents.cpp => module-services/service-time/timeEvents/TimeEvents.cpp +0 -101
@@ 1,101 0,0 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <service-time/TimeEvents.hpp>

#include <Common/Query.hpp>
#include <module-sys/Timers/TimerFactory.hpp>

#include <utility>

namespace sys
{
    class Service;
} // namespace sys

namespace stm
{
    constexpr static const int eventTimerInitInterval = 1000;

    TimeEvents::TimeEvents(sys::Service *service) : serv(service)
    {}

    TimeEvents::~TimeEvents()
    {
        stopTimer();
    }

    sys::TimerHandle &TimeEvents::timer()
    {
        if (!fireEventTimer.isValid()) {
            fireEventTimer = sys::TimerFactory::createSingleShotTimer(
                service(), timerName(), std::chrono::milliseconds{eventTimerInitInterval}, [this](sys::Timer &) {
                    fireEventTimerCallback();
                });
        }
        return fireEventTimer;
    }

    void TimeEvents::startProcessing()
    {
        timersProcessingStarted = true;
        processNextEvent();
    }

    void TimeEvents::stopProcessing()
    {
        stopTimer();
        timersProcessingStarted = false;
    }

    void TimeEvents::processNextEvent()
    {
        stopTimer();
        if (!isStarted()) {
            return;
        }

        sendNextEventQuery();
    }

    void TimeEvents::stopTimer()
    {
        timer().stop();
    }

    void TimeEvents::recreateTimer(uint32_t interval)
    {
        stopTimer();
        if (!isStarted()) {
            return;
        }

        timer().restart(std::chrono::milliseconds{interval});
    }

    void TimeEvents::fireEventTimerCallback()
    {
        if (!isStarted()) {
            return;
        }

        invokeEvent();
        sendEventFiredQuery();
    }

    bool TimeEvents::receiveNextEventQuery(std::unique_ptr<db::QueryResult> nextEventQueryResult)
    {
        if (!isStarted()) {
            return true;
        }
        if (nextEventQueryResult == nullptr) {
            return false;
        }

        uint32_t interval = calcToNextEventInterval(std::move(nextEventQueryResult));
        if (interval > 0) {
            recreateTimer(interval);
        }
        return true;
    }
} // namespace stm

M module-sys/Service/Common.hpp => module-sys/Service/Common.hpp +0 -3
@@ 22,7 22,6 @@ namespace sys
        ServiceFotaNotifications,
        AntennaNotifications,
        ServiceEvtmgrNotifications,
        CalendarNotifications,
        PhoneModeChanges,
        PhoneLockChanges,
    };


@@ 110,8 109,6 @@ inline const char *c_str(sys::BusChannel channel)
        return "AntennaNotifications";
    case sys::BusChannel::ServiceEvtmgrNotifications:
        return "ServiceEvtmgrNotifications";
    case sys::BusChannel::CalendarNotifications:
        return "CalendarNotifications";
    case sys::BusChannel::PhoneModeChanges:
        return "PhoneModeChanges";
    case sys::BusChannel::PhoneLockChanges:

M source/MessageType.hpp => source/MessageType.hpp +0 -5
@@ 136,11 136,6 @@ enum class MessageType
    FotaProgress,
    FotaFinished,

    // Time messages
    TimersProcessingStart,
    TimersProcessingStop,
    ReloadTimers,

    StateChange,
    // antenna messages
    AntennaChanged,