Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement Calendar Screen with event handling, tests, and updated dependencies #25

Merged
merged 5 commits into from
Oct 13, 2024

Conversation

othbcq
Copy link
Contributor

@othbcq othbcq commented Oct 12, 2024

Description

This pull request introduces the implementation of the Calendar Screen feature with dynamic event handling, multi-day and recurring event support, as well as UI tests. The PR also includes necessary dependency updates in the build files.

Key Features:

  • Calendar Screen:

    • Display a calendar view with selectable dates.
    • Show events for the selected day and handle multi-day events correctly.
    • Search functionality to filter and display events by name.
    • Navigate between months with smooth transitions.
  • Event Handling:

    • Support for both recurring and multi-day events.
    • Events are displayed in a list for the selected day, with details like title and date.
  • UI Tests:

    • Tests for verifying the correct display of months, navigation buttons, and day selection.
    • Search button and lookup functionality tests.
  • Dependency Updates:

    • Updated ical4j to version 3.0.21 for iCal event handling.
    • Added necessary Compose dependencies for runtime-livedata integration.

Changes:

  • Added CalendarScreen with event display and month navigation.
  • Implemented multi-day and recurring event logic in CalendarViewModel.
  • Updated gradle/libs.versions.toml with required dependencies.
  • Added UI tests in CalendarScreenTest.kt for verifying calendar functionality.

Othmane Bencheqroun added 5 commits October 12, 2024 16:23

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
- Add CalendarScreen composable to display a calendar view
- Allow users to select dates and view events dynamically
- Implement event lookup functionality with search and dialog for filtering events by name
- Handle multi-day events and indicate event occurrences visually in the calendar
- Display detailed event information including title and date in event lists
…ions

- Adjustments to project dependencies for calendar and testing features
- Update necessary plugins and build configurations
- Add ical4j version 3.0.21 for iCal integration
- Add Compose version 1.5.1 for runtime-livedata integration
- Verify the display of current month in the calendar header
- Add tests for navigating between months using the previous and next buttons
- Ensure the 'Look Up Event' button is visible and functional
- Test that selecting a day updates the selected date in the calendar
- Set up a basic structure for additional tests as Figma updates
- Implement model classes to support calendar feature integration
- Define necessary structures for handling iCal data and event processing
@othbcq othbcq linked an issue Oct 12, 2024 that may be closed by this pull request
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
29.3% Coverage on New Code (required ≥ 80%)

See analysis details on SonarCloud

Copy link
Contributor

@Kenzoud Kenzoud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, the code is well-structured, and the calendar feature looks great in the emulator. A few suggestions:

It would be great to add unit tests for the CalendarViewModel to cover its core functionalities, like filtering and sorting events, and also increase test coverage for the CalendarScreen.
Centering the "Look Up Event" button would improve the layout, and you could maybe add a little prompt that better describes the utility of the button (which might help users understand its purpose better).

Aside from these, everything looks solid. Great work!

@othbcq
Copy link
Contributor Author

othbcq commented Oct 12, 2024

Great job, the code is well-structured, and the calendar feature looks great in the emulator. A few suggestions:

It would be great to add unit tests for the CalendarViewModel to cover its core functionalities, like filtering and sorting events, and also increase test coverage for the CalendarScreen. Centering the "Look Up Event" button would improve the layout, and you could maybe add a little prompt that better describes the utility of the button (which might help users understand its purpose better).

Aside from these, everything looks solid. Great work!

Hey, thanks for the feedback!

I’ll definitely add some tests for CalendarViewModel to cover its functionalities like you mentioned, and I’ll also take a look at the UI tests to improve the coverage.

For the "Look Up Event" button, I’m not sure about centering it. I feel like it might throw off the layout a bit. I was thinking of switching it to a button with a magnifying glass icon instead. It could make it more intuitive for users. What do you think?

Appreciate the suggestions! I’ll make these changes and push an update soon.

@othbcq
Copy link
Contributor Author

othbcq commented Oct 13, 2024

I'll go ahead and merge this pull request to allow others to continue working on their parts of the project. The requested changes will be implemented as soon as possible, and I'll create a new issue to track those updates.

@othbcq othbcq merged commit 42bc06a into main Oct 13, 2024
1 of 2 checks passed
@AdrienBousquieEPFL AdrienBousquieEPFL deleted the feature/calendar branch October 13, 2024 18:00
Copy link
Collaborator

@Plouc314 Plouc314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall the PR is pretty good, the description is exhaustive (although it could be a bit shorter), the code is overall pretty good, with a good structure.

However, the PR was merged even though a PR review requested changes (and was right to do so). The Request changes option acts as a big stop sign, you should always wait for an Approve review.

CalendarViewModel

  • It is a good use of a view model, it is short but well designed.

CalendarScreen

  • There is a good modularity of the components: CalendarScreen, SearchDialog, CalendarHeader, EventItem are all great.
  • Good use of the MutableState for UI states (selectedDate, searchQuery, ...)
  • Good use of callbacks (onDateSelected, onEventClick, ...)

Other

  • You could consider creating a wrapper on VEvent as it pretty cumbersome to always having to get the property, perform null check, ...

This PR review is just feedback that we give you, if I chose this PR amongst others, it is because I have things to say on it, not because it is bad (it is not bad).


private val client = OkHttpClient()
private val _icalEvents = mutableStateListOf<VEvent>()
val icalEvents: List<VEvent> = _icalEvents
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use StateFlow in your view models (you can look into the bootcamp solution for examples):

private val _icalEvents = MutableStateFlow<List<VEvent>>(listOf())
val icalEvents: StateFlow<VEvent> = query_

Sidenote when using stateflows: always use immutable structures as it only reacts to reference changes, for example:

// good
_icalEvents.value = newList

// bad
_icalEvents.value.addAll(newList)

viewModelScope.launch {
val icalData =
fetchIcalFromUrl(
"https://p127-caldav.icloud.com/published/2/MTE0OTM4OTk2MTExNDkzOIzDRaBjEGa9_1mlmgjzcdlka5HK6EzMiIdOswU-0rZBZMDBibtH_M7CDyMpDQRQJPdGOSM0hTsS2qFNGOObsTc")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid magical values, especially when they are not trivial (1 is, MTE0OTM is not), something like this would be clearer:

fetchIcalFromUrl(ICAL_URL)

while (current.before(eventEndDate)) {
val multiDayEventInstance = eventInstance.copy() as VEvent
multiDayEventInstance.getProperty<DtStart>(Property.DTSTART)?.date =
DateTime(current.time)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The methods handleRecurringEvents is missing documentation, both for the method itself and in the code, for example: why do we need to make a copy of eventInstance and change its DTSTART property ? In addition, the code structure is complicated with a while loop inside an if inside a for loop, consider refactoring or adding comments.
Also, the function takes a mutable list allEvents as argument and modify it, while this is good for performance (i.e. it's what you would do in C), it is something you want to avoid, as the function is then performing side-effects, which are known to introduce bugs and be harder to reason about.

Here is an example of how handleRecurringEvents could be refactored:

  /**
   * General doc about the function
   */
  private fun handleRecurringEvents(
      component: VEvent,
      period: Period,
      eventStartDate: Date,
      eventEndDate: Date,
  ): List<VEvent> {
    val events = mutableListOf<VEvent>()
    val recurrenceSet = component.calculateRecurrenceSet(period)

    for (recurrence in recurrenceSet) {
      val eventInstance = component.copy() as VEvent
      eventInstance.getProperty<DtStart>(Property.DTSTART)?.date = recurrence.start

      if (!eventEndDate.after(recurrence.start)) {
        // comment on why no while loop
        events.add(eventInstance)
        continue
      }

      var current = recurrence.start
      while (current.before(eventEndDate)) {

        // comment on why this
        val multiDayEventInstance = eventInstance.copy() as VEvent
        multiDayEventInstance.getProperty<DtStart>(Property.DTSTART)?.date =
            DateTime(current.time)
        events.add(multiDayEventInstance)

        // comment add a day, or better use an helper function
        val calendarInstance =
            JavaCalendar.getInstance().apply {
              time = current
              add(JavaCalendar.DATE, 1)
            }
        current = DateTime(calendarInstance.time)
      }
    }
    return events
  }

}
}

private fun handleNonRecurringEvents(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as for handleRecurringEvents method, it is missing documentation and taking a mutable list as argument.

_icalEvents.clear()
_icalEvents.addAll(allEvents)
Log.d("CalendarViewModel", "Total events parsed: ${_icalEvents.size}")
} catch (e: Exception) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This try-catch block is very long and it catches all exceptions subtype of Exception, try to isolate the code which may throw the exception and restrict the catch block to catch more specific exception if applicable (for example IOException)

}

@Composable
fun EventList(calendarViewModel: CalendarViewModel, selectedDate: Date) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having both EventListWithResults and EventList components, you could have one component that would take a title, list of events, and onEventClick as arguments, to have a more modular approach, something like that:

@Composable
fun EventList(title: String, events: List<VEvent>, onEventClick: (Date) -> Unit) {
  Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
    Text(
        text = title,
        style = MaterialTheme.typography.titleMedium,
        fontWeight = FontWeight.Bold,
        modifier = Modifier.padding(bottom = 8.dp))

    if (events.isNotEmpty()) {
      events.forEach { event ->
        val eventDate = event.getProperty<DtStart>(Property.DTSTART)?.date as Date
        EventItem(event = event, onClick = { onEventClick(eventDate) })
        Spacer(modifier = Modifier.height(8.dp))
      }
    } else {
      Text(
          text = "No events.",
          style = MaterialTheme.typography.bodyMedium,
          color = MaterialTheme.colorScheme.onSurfaceVariant)
    }
  }
}

fun CalendarGrid(
selectedDate: Date,
onDateSelected: (Date) -> Unit,
calendarViewModel: CalendarViewModel
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of passing the view model instance to then use the icalEvents attribute, consider passing a list of VEvent, icalEvents. The idea is to pass the least general object down, as it then clear that CalendarGrid only need the ical events list to someone not familiar with CalendarGrid and it is simplier to test.


val isSelected = isSameDay(date, selectedDate)

Box(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider creating a component here, to simplify the CalendarGrid method.

cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)
}

fun updateMonth(date: Date, months: Int): Date {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider creating a DateUtils module to group all helper functions for dates, so that they can be reused in other files. That way you can also keep track of all already defined helper functions to avoid duplicates.

composeTestRule.onNodeWithText("Look Up Event").assertIsDisplayed()
}

// Add more tests when figma is updated with all the components and testTags
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's nothing more permanent than a temporary solution - Oswald Maskens (I must give credits to the guy)

Either create the tests now or don't basically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement Calendar of Astronomical Events
3 participants