diff --git a/Userland/Applications/Calendar/AddEventDialog.cpp b/Userland/Applications/Calendar/AddEventDialog.cpp index cf676a04e2..8cb3d0fb1f 100644 --- a/Userland/Applications/Calendar/AddEventDialog.cpp +++ b/Userland/Applications/Calendar/AddEventDialog.cpp @@ -112,25 +112,13 @@ AddEventDialog::AddEventDialog(Core::DateTime date_time, EventManager& event_man ErrorOr AddEventDialog::add_event_to_calendar() { - auto to_date_string = [](Core::DateTime date_time) -> ErrorOr { - return String::formatted("{}-{:0>2d}-{:0>2d}", date_time.year(), date_time.month(), date_time.day()); - }; - auto to_time_string = [](Core::DateTime date_time) -> ErrorOr { - return String::formatted("{}:{:0>2d}", date_time.hour(), date_time.minute()); - }; - - JsonObject event; - auto start_date = TRY(to_date_string(m_start_date_time)); - auto start_time = TRY(to_time_string(m_start_date_time)); - auto end_date = TRY(to_date_string(m_end_date_time)); - auto end_time = TRY(to_time_string(m_end_date_time)); auto summary = find_descendant_of_type_named("event_title_textbox")->get_text(); - event.set("start_date", JsonValue(start_date)); - event.set("start_time", JsonValue(start_time)); - event.set("end_date", JsonValue(end_date)); - event.set("end_time", JsonValue(end_time)); - event.set("summary", JsonValue(summary)); - TRY(m_event_manager.add_event(event)); + m_event_manager.add_event(Event { + .summary = TRY(String::from_byte_string(summary)), + .start = m_start_date_time, + .end = m_end_date_time, + }); + m_event_manager.set_dirty(true); return {}; diff --git a/Userland/Applications/Calendar/EventCalendar.cpp b/Userland/Applications/Calendar/EventCalendar.cpp index 807e413ee8..a453eebb1b 100644 --- a/Userland/Applications/Calendar/EventCalendar.cpp +++ b/Userland/Applications/Calendar/EventCalendar.cpp @@ -25,28 +25,28 @@ void EventCalendar::paint_tile(GUI::Painter& painter, GUI::Calendar::Tile& tile, { Calendar::paint_tile(painter, tile, tile_rect, x_offset, y_offset, day_offset); - auto events = m_event_manager->events(); + if (tile.width < tile_breakpoint || tile.height < tile_breakpoint) + return; - if (tile.width > tile_breakpoint && tile.height > tile_breakpoint) { - auto index = 0; - auto font_height = font().x_height(); - events.for_each([&](JsonValue const& value) { - auto const& event = value.as_object(); + auto index = 0; + auto font_height = font().x_height(); + for (auto const& event : m_event_manager->events()) { + auto start = event.start; + if (start.year() == tile.year && start.month() == tile.month && start.day() == tile.day) { + auto text_rect = tile.rect.translated(4, 4 + (font_height + 4) * ++index); - if (!event.has("start_date"sv) || !event.has("start_date"sv) || !event.has("summary"sv)) - return; + auto event_text = String::formatted("{} {}", start.to_byte_string("%H:%M"sv), event.summary); + if (event_text.is_error()) + continue; - auto start_date = event.get("start_date"sv).value().to_byte_string(); - auto start_time = event.get("start_time"sv).value().to_byte_string(); - auto summary = event.get("summary"sv).value().to_byte_string(); - auto combined_text = ByteString::formatted("{} {}", start_time, summary); - - if (start_date == ByteString::formatted("{}-{:0>2d}-{:0>2d}", tile.year, tile.month, tile.day)) { - - auto text_rect = tile.rect.translated(4, 4 + (font_height + 4) * ++index); - painter.draw_text(text_rect, combined_text, Gfx::FontDatabase::default_font(), Gfx::TextAlignment::TopLeft, palette().base_text(), Gfx::TextElision::Right); - } - }); + painter.draw_text( + text_rect, + event_text.release_value(), + Gfx::FontDatabase::default_font(), + Gfx::TextAlignment::TopLeft, + palette().base_text(), + Gfx::TextElision::Right); + } } } diff --git a/Userland/Applications/Calendar/EventManager.cpp b/Userland/Applications/Calendar/EventManager.cpp index d0df59103c..73544ad0b4 100644 --- a/Userland/Applications/Calendar/EventManager.cpp +++ b/Userland/Applications/Calendar/EventManager.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, the SerenityOS developers. + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -11,6 +12,8 @@ namespace Calendar { +static constexpr StringView DATE_FORMAT = "%Y-%m-%dT%H:%M:%S"sv; + EventManager::EventManager() { } @@ -20,16 +23,14 @@ OwnPtr EventManager::create() return adopt_own(*new EventManager()); } -ErrorOr EventManager::add_event(JsonObject event) +void EventManager::add_event(Event event) { - TRY(m_events.append(move(event))); + m_events.append(move(event)); set_dirty(true); on_events_change(); - - return {}; } -void EventManager::set_events(JsonArray events) +void EventManager::set_events(Vector events) { m_events = move(events); on_events_change(); @@ -41,21 +42,65 @@ ErrorOr EventManager::save(FileSystemAccessClient::File& file) set_dirty(false); auto stream = file.release_stream(); - TRY(stream->write_some(m_events.to_byte_string().bytes())); + auto json = TRY(serialize_events()); + TRY(stream->write_some(json.to_byte_string().bytes())); stream->close(); return {}; } +ErrorOr EventManager::serialize_events() +{ + JsonArray result; + for (auto const& event : m_events) { + JsonObject object; + object.set("start", JsonValue(event.start.to_byte_string(DATE_FORMAT))); + object.set("end", JsonValue(event.end.to_byte_string(DATE_FORMAT))); + object.set("summary", JsonValue(event.summary)); + TRY(result.append(object)); + } + + return result; +} + +ErrorOr> EventManager::deserialize_events(JsonArray const& json) +{ + Vector result; + + for (auto const& value : json.values()) { + auto const& object = value.as_object(); + if (!object.has("summary"sv) || !object.has("start"sv) || !object.has("end"sv)) + continue; + + auto summary = TRY(String::from_byte_string(object.get("summary"sv).release_value().as_string())); + auto start = Core::DateTime::parse(DATE_FORMAT, object.get("start"sv).release_value().as_string()); + if (!start.has_value()) + continue; + + auto end = Core::DateTime::parse(DATE_FORMAT, object.get("end"sv).release_value().as_string()); + if (!end.has_value()) + continue; + + Event event = { + .summary = summary, + .start = start.release_value(), + .end = end.release_value(), + }; + result.append(event); + } + + return result; +} + ErrorOr EventManager::load_file(FileSystemAccessClient::File& file) { set_filename(file.filename()); set_dirty(false); auto content = TRY(file.stream().read_until_eof()); - auto events = TRY(AK::JsonParser(content).parse()); - - set_events(events.as_array()); + auto json = TRY(AK::JsonParser(content).parse()); + auto events = TRY(deserialize_events(json.as_array())); + set_events(events); return {}; } diff --git a/Userland/Applications/Calendar/EventManager.h b/Userland/Applications/Calendar/EventManager.h index 2db3cd01d8..f1dea3c492 100644 --- a/Userland/Applications/Calendar/EventManager.h +++ b/Userland/Applications/Calendar/EventManager.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2023, the SerenityOS developers. + * Copyright (c) 2023, David Ganz * * SPDX-License-Identifier: BSD-2-Clause */ @@ -10,11 +11,18 @@ #include #include #include +#include #include #include namespace Calendar { +struct Event { + String summary; + Core::DateTime start; + Core::DateTime end; +}; + class EventManager { AK_MAKE_NONCOPYABLE(EventManager); AK_MAKE_NONMOVABLE(EventManager); @@ -29,18 +37,21 @@ public: ErrorOr save(FileSystemAccessClient::File& file); ErrorOr load_file(FileSystemAccessClient::File& file); - ErrorOr add_event(JsonObject); - void set_events(JsonArray events); + void add_event(Event); + void set_events(Vector); void clear() { m_events.clear(); } - JsonArray const& events() const { return m_events; } + Span events() const { return m_events.span(); } Function on_events_change; private: explicit EventManager(); - JsonArray m_events; + ErrorOr serialize_events(); + ErrorOr> deserialize_events(JsonArray const& json); + + Vector m_events; String m_current_filename; bool m_dirty { false };