Skip to content

[#352] update exceptions on all update without reseting them#510

Merged
Eriikah merged 7 commits intomainfrom
352-recurring-event-only-date-changes-should-reset-recurring-event-exceptions
Feb 11, 2026
Merged

[#352] update exceptions on all update without reseting them#510
Eriikah merged 7 commits intomainfrom
352-recurring-event-only-date-changes-should-reset-recurring-event-exceptions

Conversation

@Eriikah
Copy link
Collaborator

@Eriikah Eriikah commented Feb 4, 2026

related to #352
docker image on eriikaah/twake-calendar-front:issue-352-recurring-event-only-date-changes-should-reset-recurring-event-exceptions

Summary by CodeRabbit

  • New Features
    • Option to update a recurring series by either propagating metadata to exceptions or replacing exceptions entirely; improved, more reliable series updater.
  • Bug Fixes
    • Better propagation of metadata (summary, description, location, privacy, transparency, attendees, organizer, videoconference) while preserving exception-specific customizations.
    • VALARM handling and SEQUENCE increments now apply correctly across exceptions.
  • Tests
    • Added extensive tests covering many recurring-update scenarios.

@Eriikah Eriikah self-assigned this Feb 4, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

Warning

Rate limit exceeded

@Eriikah has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 59 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

Walkthrough

Replaces the internal EventApi update logic by adding a new exported updateSeries implementation at src/features/Events/api/updateSeries.ts that reconstructs the master VEVENT, detects metadata and VALARM changes, increments SEQUENCE when needed, applies changes across VEVENTs while preserving override-specific fields (or removes overrides based on a flag), and persists the updated VCalendar (including VTIMEZONE) via PUT. Adjusts code to use public calendar types (VCalComponent, VObjectProperty, VObjectValue) and makes getAllRecurrentEvent exported. Removes the old internal updateSeries and updates import callsites.

Possibly related PRs

Suggested labels

merge_wanted

Suggested reviewers

  • lenhanphung
  • chibenwa
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: updating exceptions (overrides) on all updates without resetting them, which aligns with the core refactoring of updateSeries logic.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 352-recurring-event-only-date-changes-should-reset-recurring-event-exceptions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/features/Events/EventApi.ts`:
- Around line 354-410: The current mapper returns [veventType, newProps] and
drops vevent[2] (subcomponents like VALARM); update the return to include the
original components array by destructuring vevent into [veventType, props,
components] (or reference vevent[2]) and produce a final components array that
filters out old VALARM (and other subcomponents you intend to replace) and
appends the updated master subcomponents from updatedMaster[2]; then return
[veventType, newProps, finalComponents] so alarms/nested components are
preserved and replaced correctly.

Copy link
Member

@chibenwa chibenwa left a comment

Choose a reason for hiding this comment

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

Works like a charm.

But... No tests??? I'd like we have some so that we avoid regressions on the topic!

@Eriikah
Copy link
Collaborator Author

Eriikah commented Feb 6, 2026

Works like a charm.

But... No tests??? I'd like we have some so that we avoid regressions on the topic!

Of course! I was waiting for a review to be sure that the logic didn't have any flaw before adding tests

@Eriikah Eriikah force-pushed the 352-recurring-event-only-date-changes-should-reset-recurring-event-exceptions branch from a8aa7f9 to 41d140a Compare February 6, 2026 14:02
@Eriikah Eriikah requested a review from lenhanphung February 9, 2026 10:16
@Eriikah Eriikah force-pushed the 352-recurring-event-only-date-changes-should-reset-recurring-event-exceptions branch from 41d140a to 4d4e8fd Compare February 10, 2026 10:15
Copy link
Member

@chibenwa chibenwa left a comment

Choose a reason for hiding this comment

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

Still work like a charm.

But IMAP we can do a better job at writing clearer code here. I'd suggest to invest in small housekeeping refactorings.

@@ -361,9 +363,106 @@ export const updateSeries = async (
// When date/time/timezone/repeat rules changed, remove all override instances
finalVevents = [updatedMaster];
} else {
Copy link
Member

Choose a reason for hiding this comment

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

This else block is too long and to complex

We need to try to extract sub blocks of independant logical units in order to make it easier to understand and more natural.

Comment on lines +382 to +391
const metadataFields = [
"summary",
"description",
"location",
"class",
"transp",
"attendee",
"organizer",
"x-openpaas-videoconference",
];
Copy link
Member

Choose a reason for hiding this comment

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

constant can likely be defined outside of the methid body to keep it short.

Comment on lines +376 to +377
// Detect which fields changed in the master
const changedFields = new Map<string, VCalComponent[]>();
Copy link
Member

Choose a reason for hiding this comment

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

This likely can be a separated method cf earlier comment

}
});

// Check if VALARM component changed
Copy link
Member

Choose a reason for hiding this comment

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

Idem

);
const valarmChanged = serialize(oldValarm) !== serialize(newValarm);

const updatedVevents = vevents.map((vevent, index) => {
Copy link
Member

Choose a reason for hiding this comment

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

The inner .map{...}) can likely be extracted.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/features/Events/api/updateSeries.ts`:
- Around line 100-122: The sequence field is being stored as a string in
incrementSequenceNumber which causes type mismatch with makeVevent (numeric);
modify incrementSequenceNumber so that the sequence value in the VObjectProperty
tuple is a number (set newProps[sequenceIndex][3] to currentSequence + 1 as a
number) and when adding a new sequence property push the integer 1 (not "1");
target the incrementSequenceNumber function and update the 4th element of the
sequence tuple (index 3) to be numeric to match makeVevent/event.sequence
handling.
- Around line 227-230: The code uses a non-null assertion on rrule when pushing
into updatedMaster[1], which can push undefined if findFieldValue returned
undefined; change the logic in updateSeries (around updatedMaster, newRrule and
rrule) to guard against undefined by checking if rrule is truthy before pushing
(or create/derive a safe default or throw a clear error), i.e., only push rrule
into updatedMaster[1] when rrule !== undefined, and otherwise handle the missing
RRULE explicitly (log/throw or generate a default) to avoid inserting undefined.
🧹 Nitpick comments (2)
src/features/Events/api/updateSeries.ts (1)

33-35: JSON.stringify comparison is order-sensitive for object keys.

serialize uses JSON.stringify, which is sensitive to key insertion order in the parameter objects of VObjectProperty. If the old and new objects have the same keys in different orders (e.g., {cn: "a", partstat: "b"} vs {partstat: "b", cn: "a"}), they'll be considered different even though they're semantically equal. This could cause unnecessary propagation to overrides but won't lose data — just a spurious sequence bump.

__test__/features/Events/EventApi.test.tsx (1)

20-24: vevent helper is defined but never used in the tests.

All tests construct ICS strings directly and let ICAL.parse produce the jCal structures. This helper can be removed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/features/Events/api/updateSeries.ts`:
- Around line 232-233: timezoneData is accessed directly from
TIMEZONES.zones[event.timezone] and can be undefined; add a guard before calling
makeTimezone to handle missing data: check if timezoneData (from
TIMEZONES.zones[event.timezone]) is present and if not either use a safe
fallback timezone (e.g., UTC) or skip creating vtimezone and handle downstream
logic accordingly, and surface/log a clear error/warning; update the code around
timezoneData and the call to makeTimezone/vtimezone to use this guarded path so
makeTimezone is never invoked with undefined.
- Around line 250-254: The vcalendar jCal is being created with an empty
properties array (e.g. the newJCal in updateSeries.ts) which drops required
VERSION and PRODID; update the construction to copy or synthesize the VERSION
and PRODID properties from the incoming calendar's vcalendar properties (e.g.
read from the original vcalendar component/properties alongside vtimezone and
finalVevents) and include them in the properties array instead of leaving it
empty. Apply the same change where vcalendar jCal objects are built elsewhere
(references: eventUtils.ts, EventApi.ts occurrences and updateSeries.ts newJCal)
so each constructed vcalendar includes the VERSION and PRODID entries taken from
the source calendar data.
🧹 Nitpick comments (2)
src/features/Events/api/updateSeries.ts (2)

228-230: Redundant non-null assertion on rrule.

The condition on line 228 already guards that rrule is truthy, so the ! on line 229 is unnecessary.

Proposed fix
   if (!newRrule && rrule) {
-    updatedMaster[1].push(rrule!);
+    updatedMaster[1].push(rrule);
   }

203-211: Cast as VCalComponent[] on line 208 is unchecked.

The return type of getAllRecurrentEvent is cast without validation. If the function ever returns a different shape (e.g., includes non-VEVENT components or an unexpected structure), this could silently produce incorrect behavior downstream.

This is acceptable if getAllRecurrentEvent is well-tested and guaranteed to return only VEVENT components, but worth a brief note.

Comment on lines +232 to +233
const timezoneData = TIMEZONES.zones[event.timezone];
const vtimezone = makeTimezone(timezoneData, event);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

No guard against missing timezone data.

If event.timezone doesn't match any key in TIMEZONES.zones, timezoneData will be undefined, likely causing makeTimezone to throw or produce an invalid VTIMEZONE component.

Proposed defensive check
   const timezoneData = TIMEZONES.zones[event.timezone];
+  if (!timezoneData) {
+    throw new Error(`Unknown timezone: ${event.timezone}`);
+  }
   const vtimezone = makeTimezone(timezoneData, event);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const timezoneData = TIMEZONES.zones[event.timezone];
const vtimezone = makeTimezone(timezoneData, event);
const timezoneData = TIMEZONES.zones[event.timezone];
if (!timezoneData) {
throw new Error(`Unknown timezone: ${event.timezone}`);
}
const vtimezone = makeTimezone(timezoneData, event);
🤖 Prompt for AI Agents
In `@src/features/Events/api/updateSeries.ts` around lines 232 - 233, timezoneData
is accessed directly from TIMEZONES.zones[event.timezone] and can be undefined;
add a guard before calling makeTimezone to handle missing data: check if
timezoneData (from TIMEZONES.zones[event.timezone]) is present and if not either
use a safe fallback timezone (e.g., UTC) or skip creating vtimezone and handle
downstream logic accordingly, and surface/log a clear error/warning; update the
code around timezoneData and the call to makeTimezone/vtimezone to use this
guarded path so makeTimezone is never invoked with undefined.

Comment on lines +250 to +254
const newJCal = [
"vcalendar",
[],
[...finalVevents, vtimezone.component.jCal],
];
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check if the file exists and read the relevant lines
if [ -f "src/features/Events/api/updateSeries.ts" ]; then
  echo "=== File found. Reading lines 245-260 ==="
  sed -n '245,260p' "src/features/Events/api/updateSeries.ts" | cat -n
  echo ""
else
  echo "File not found at expected path"
fi

# Check for vcalendar constructions throughout the codebase
echo "=== Searching for vcalendar jCal constructions ==="
rg -n '"vcalendar"' --type=ts -B2 -A4

Repository: linagora/twake-calendar-frontend

Length of output: 6770


🏁 Script executed:

#!/bin/bash
# Also search for any existing prodid or version assignments in the codebase
echo "=== Searching for prodid usage ==="
rg -in 'prodid' --type=ts

echo ""
echo "=== Searching for version assignments in jCal context ==="
rg -n '"version".*"2\.0"' --type=ts -B2 -A2

Repository: linagora/twake-calendar-frontend

Length of output: 296


🏁 Script executed:

#!/bin/bash
# Search for jCal to iCalendar conversion or serialization
echo "=== Searching for jCal serialization/conversion ==="
rg -n 'JSON.stringify|serialize|toIcsString|toICal|ICAL' --type=ts -B2 -A2 | head -100

echo ""
echo "=== Check CalendarTypes.ts for prodid usage ==="
cat -n src/features/Calendars/CalendarTypes.ts | head -30

echo ""
echo "=== Search for ICAL library imports ==="
rg -n 'from.*ical|import.*ical' --type=ts

Repository: linagora/twake-calendar-frontend

Length of output: 6883


🏁 Script executed:

#!/bin/bash
# Check test file to see what's expected
echo "=== Checking EventApi test for PRODID context ==="
rg -n -B5 -A5 'PRODID.*Test' __test__/features/Events/EventApi.test.tsx

echo ""
echo "=== Check if there's any jCal property handling in eventUtils ==="
rg -n 'version|prodid' src/features/Events/eventUtils.ts -i

Repository: linagora/twake-calendar-frontend

Length of output: 445


🏁 Script executed:

#!/bin/bash
# Check if there's any conversion from jCal to iCalendar text before sending to API
echo "=== Searching for toICSString or toICS conversions ==="
rg -n 'toICSString|toICS|toText|toString' --type=ts src/features/Events/ -B2 -A2

echo ""
echo "=== Check the api function to understand what happens to the JSON.stringify(newJCal) ==="
rg -n 'function api|const api' src/features/ --type=ts | head -20

echo ""
echo "=== Check if Calendar prodid/version are ever used when creating events ==="
rg -n 'calendar\.prodid|calendar\.version' --type=ts -B2 -A2

Repository: linagora/twake-calendar-frontend

Length of output: 1886


🏁 Script executed:

#!/bin/bash
# Look at how the Calendar data is used and if prodid/version come from server
echo "=== Search where Calendar objects are created/fetched ==="
rg -n 'prodid.*=|version.*=' src/features/Calendars/ --type=ts -B2 -A2 | head -80

echo ""
echo "=== Check addSharedCalendarAsync since learning mentions it ==="
head -50 src/features/Calendars/services/addSharedCalendarAsync.ts

Repository: linagora/twake-calendar-frontend

Length of output: 1898


🏁 Script executed:

#!/bin/bash
# Look at the api function implementation
echo "=== Find where 'api' function is defined ==="
fd 'api\.(ts|tsx)$' src/ | head -10

echo ""
echo "=== Search for fetch/axios/request patterns in api calls ==="
rg -n 'fetch|axios|request' src/features/Events/api/ --type=ts -B1 -A3 | head -60

echo ""
echo "=== Check if ICAL.Component or jCal.toICS is ever used ==="
rg -n 'ICAL.Component|\.toICS|\.toString|toJCal|fromJCal' --type=ts

Repository: linagora/twake-calendar-frontend

Length of output: 1507


🏁 Script executed:

#!/bin/bash
# Check EventApi.ts to see how the api is called with newJCal
echo "=== Look at EventApi.ts around the updateSeries context ==="
sed -n '250,270p' src/features/Events/api/updateSeries.ts | cat -n

echo ""
echo "=== Check what happens in the actual API call in EventApi.ts ==="
sed -n '190,210p' src/features/Events/EventApi.ts | cat -n

echo ""
echo "=== Look for any ical.js API calls that convert jCal ==="
rg -n 'new ICAL|ICAL\.' src/features/Events/ --type=ts -A1 | head -80

Repository: linagora/twake-calendar-frontend

Length of output: 2048


🏁 Script executed:

#!/bin/bash
# Find the api function definition
echo "=== Locate and read the api function ==="
rg -n 'export.*const api|function api' src/ --type=ts -B2 -A15 | head -80

echo ""
echo "=== Check EventApi.ts to see how it imports/uses the api function ==="
head -20 src/features/Events/EventApi.ts | cat -n

Repository: linagora/twake-calendar-frontend

Length of output: 1929


🏁 Script executed:

#!/bin/bash
# Check if there's middleware or any post-processing of the jCal before sending
echo "=== Search for any function that might convert jCal to iCal text ==="
rg -n 'toICAL|toText|Component\.toJCal|serialize' src/ --type=ts -B2 -A2

echo ""
echo "=== Look for the actual API utils file ==="
find src -name '*api*' -type f -name '*.ts' -o -name '*.tsx' | grep -v node_modules | head -20

Repository: linagora/twake-calendar-frontend

Length of output: 2253


🏁 Script executed:

#!/bin/bash
# Check if jCal is actually sent as JSON and what the server expects
echo "=== Check EventApi.ts imports and overall structure ==="
head -30 src/features/Events/EventApi.ts | cat -n

echo ""
echo "=== Check the actual api utility ==="
head -50 src/utils/apiUtils.ts | cat -n

Repository: linagora/twake-calendar-frontend

Length of output: 3098


🏁 Script executed:

#!/bin/bash
# Check if jCal should be converted to iCalendar text before sending
echo "=== Search for ICAL.Component or toICSString usage ==="
rg -n 'ICAL.Component\(' src/ --type=ts -B3 -A5

echo ""
echo "=== Check if there's any jCal to iCal string conversion in the codebase ==="
rg -n 'toICS|toICAL|\.toString|.toCal' src/ --type=ts

echo ""
echo "=== Look for how ICAL library is expected to be used for serialization ==="
rg -n 'ICAL\..*\.jCal|jCal.*ICAL' src/ --type=ts -B2 -A2

Repository: linagora/twake-calendar-frontend

Length of output: 1650


🏁 Script executed:

#!/bin/bash
# Most importantly: check what format the API actually expects
echo "=== Check CalendarApi.ts for how it sends calendar data ==="
grep -n "JSON.stringify\|body:" src/features/Calendars/CalendarApi.ts -B3 -A3 | head -80

echo ""
echo "=== Check if there are integration tests showing actual payload format ==="
find __test__ -name "*.test.*" -exec grep -l "VCALENDAR\|vcalendar" {} \; | head -5

Repository: linagora/twake-calendar-frontend

Length of output: 1378


🏁 Script executed:

#!/bin/bash
# Check the test file to understand what payload format is expected
echo "=== Look at EventApi.test.tsx for updateSeries payload format ==="
grep -n "updateSeries\|VCALENDAR\|vcalendar" __test__/features/Events/EventApi.test.tsx -B5 -A10 | head -100

Repository: linagora/twake-calendar-frontend

Length of output: 3353


🏁 Script executed:

#!/bin/bash
# Check if jCal needs to be converted to iCalendar text
echo "=== Search for how jCal data flows through the system ==="
grep -n "JSON.stringify(newJCal)\|JSON.stringify.*jCal" src/features/Events/ -r --include="*.ts" -B3 -A3

echo ""
echo "=== Look for any test that shows the actual request format ==="
grep -n "body.*vcalendar\|body.*version\|body.*prodid" __test__/features/Events/ -r --include="*.tsx" -i -B2 -A2 | head -60

Repository: linagora/twake-calendar-frontend

Length of output: 1937


🏁 Script executed:

#!/bin/bash
# Check how the incoming calendar data with VERSION/PRODID is parsed
echo "=== Search for where incoming iCalendar is parsed ==="
grep -n "ICAL.parse\|eventical\|jcal.*=" src/features/Events/ --include="*.ts" -A3 -B2

echo ""
echo "=== Check if properties like version/prodid are ever extracted and stored ==="
grep -n "version\|prodid" src/features/Events/eventUtils.ts -i -B2 -A2

echo ""
echo "=== Check the jCal structure in tests to see what's expected ==="
grep -n "\[1\]\|properties\|version\|prodid" __test__/features/Events/eventUtils.test.ts | head -30

Repository: linagora/twake-calendar-frontend

Length of output: 876


🏁 Script executed:

#!/bin/bash
# The key question: when the server receives this jCal as JSON, what does it do with it?
# Check if there's documentation or if we can infer from how parsing works
echo "=== Look at how ICAL.parse handles the incoming data ==="
grep -n "ICAL.parse" src/features/Events/EventApi.ts -B5 -A10

echo ""
echo "=== Check if there's any mention of how jCal should be structured ==="
grep -rn "jCal\|jcal" src/ --include="*.ts" --include="*.tsx" | grep -i "structure\|format\|property" | head -20

Repository: linagora/twake-calendar-frontend

Length of output: 1292


🏁 Script executed:

#!/bin/bash
# Verify RFC 5545 compliance and check if server really expects jCal JSON or iCal text
echo "=== Check if there are any comments or docs about the API format ==="
grep -rn "CalDAV\|iCalendar\|RFC.*5545\|VCALENDAR format" src/ --include="*.ts" --include="*.tsx" | head -20

echo ""
echo "=== Look at the actual test mock to understand what the server expects ==="
sed -n '310,330p' __test__/features/Events/EventApi.test.tsx | cat -n

echo ""
echo "=== Check if jCal is ever used with ICAL.Component ==="
grep -n "new ICAL.Component\|\.component\.jCal" src/ --include="*.ts" -B2 -A2

Repository: linagora/twake-calendar-frontend

Length of output: 1586


🏁 Script executed:

#!/bin/bash
# Search for any documentation or comments about the expected format
echo "=== Look for backend/server configuration or API docs ==="
find . -name "*.md" -o -name "*.txt" -o -name "*.json" | xargs grep -l "VCALENDAR\|CalDAV\|jCal" 2>/dev/null | head -10

echo ""
echo "=== Check if Calendar data from server includes version/prodid ==="
grep -rn '"version"\|"prodid"' src/features/Calendars/ --include="*.ts" | head -20

Repository: linagora/twake-calendar-frontend

Length of output: 301


🏁 Script executed:

#!/bin/bash
# Critical check: Does the code actually work? Check if there are any test failures or server errors
echo "=== Look at test results for updateSeries ==="
grep -n "it\|describe\|expect\|pass\|fail" __test__/features/Events/EventApi.test.tsx | grep -A5 -B5 "updateSeries" | head -40

echo ""
echo "=== Check if any tests are skipped or show known issues with version/prodid ==="
grep -n "skip\|xdescribe\|xit\|TODO.*version\|TODO.*prodid\|FIXME" __test__/features/Events/EventApi.test.tsx

Repository: linagora/twake-calendar-frontend

Length of output: 2133


🏁 Script executed:

#!/bin/bash
# Check RFC 7265 (jCal format) vs RFC 5545 to understand if version/prodid are required in jCal
echo "=== Search for any jCal spec or format documentation in comments ==="
grep -rn "jCal\|JSON.*Calendar" src/ --include="*.ts" -B2 -A2 | head -40

echo ""
echo "=== Most important: Check if incoming events with version/prodid are stored or used ==="
grep -n "eventical\[1\]\|jcal\[1\]" src/features/Events/EventApi.ts -B3 -A3

Repository: linagora/twake-calendar-frontend

Length of output: 2445


🏁 Script executed:

#!/bin/bash
# Let me check if the server actually rejects the current format or if tests show it works
echo "=== Look for any error handling or server response about missing version/prodid ==="
grep -rn "400\|422\|422\|invalid\|required" __test__/features/Events/ --include="*.tsx" | head -20

echo ""
echo "=== Check if the incoming parsed event preserves version and prodid ==="
sed -n '35,50p' src/features/Events/EventApi.ts | cat -n

echo ""
echo "=== Specifically check what eventical[1] contains (the properties array) ==="
grep -n "eventical\[0\]\|eventical\[1\]\|eventical\[2\]" src/features/Events/EventApi.ts -B2 -A2

Repository: linagora/twake-calendar-frontend

Length of output: 1360


🏁 Script executed:

#!/bin/bash
# The critical check: how is the original version/prodid preserved or lost?
echo "=== See if we parse and preserve vcalendar properties from incoming data ==="
cat src/features/Events/EventApi.ts | grep -n "vcalendar\|eventical\|jcal" | head -30

echo ""
echo "=== Check the calendarEventToJCal function ==="
grep -n "calendarEventToJCal" src/features/Events/eventUtils.ts -A20

Repository: linagora/twake-calendar-frontend

Length of output: 1309


🏁 Script executed:

#!/bin/bash
# Final verification: confirm that eventical[1] is never extracted or preserved
echo "=== Verify the complete jCal structure when incoming ==="
python3 << 'EOF'
# According to RFC 7265 (jCal spec) and RFC 5545:
# jCal structure: ["vcalendar", [properties], [components]]
# When ICAL.parse parses iCalendar text like:
# BEGIN:VCALENDAR
# VERSION:2.0
# PRODID:-//Test//EN
# ...
# END:VCALENDAR
# 
# It creates: ["vcalendar", [["version", {}, "text", "2.0"], ["prodid", {}, "text", "-//Test//EN"], ...], [components...]]
#
# But the code builds: ["vcalendar", [], [components...]]
# So it's definitely losing version and prodid

print("RFC 5545 requires VERSION and PRODID in VCALENDAR")
print("jCal format (RFC 7265): [name, properties_array, components_array]")
print("Current code creates: ['vcalendar', [], [vevents, vtimezone]]")
print("Missing properties array contents!")
EOF

Repository: linagora/twake-calendar-frontend

Length of output: 346


🏁 Script executed:

#!/bin/bash
# Check if the incoming data has version/prodid that gets lost
echo "=== Verify data loss - parse test data to show structure ==="
cat > /tmp/test_parse.py << 'EOF'
# Simulating what ICAL.parse would return for the test input
test_ics = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Test//EN
BEGIN:VEVENT
UID:event1
DTSTART:20240201T100000Z
DTEND:20240201T110000Z
RRULE:FREQ=DAILY
SUMMARY:Old title
SEQUENCE:1
END:VEVENT
END:VCALENDAR"""

print("Test ICS has:")
print("- VERSION:2.0")
print("- PRODID:-//Test//EN")
print("")
print("When ICAL.parse() processes this, the result jCal would be:")
print('["vcalendar",')
print('  [["version", {}, "text", "2.0"],')
print('   ["prodid", {}, "text", "-//Test//EN"]],')
print('  [vevent_components...]]')
print("")
print("But the code creates:")
print('["vcalendar", [], [vevent_components...]]')
print("")
print("The properties array [1] is completely discarded!")
EOF
python3 /tmp/test_parse.py

Repository: linagora/twake-calendar-frontend

Length of output: 480


Add required VERSION and PRODID properties to vcalendar jCal object.

RFC 5545 requires these fields in a VCALENDAR object. The current code creates the vcalendar jCal with an empty properties array, discarding the version and prodid that come from the incoming calendar data. This pattern appears in five locations across the codebase: eventUtils.ts, EventApi.ts (three occurrences), and updateSeries.ts. Systematically add these required properties:

Proposed fix
   const newJCal = [
     "vcalendar",
-    [],
+    [
+      ["version", {}, "text", "2.0"],
+      ["prodid", {}, "text", "-//Twake//Calendar//EN"],
+    ],
     [...finalVevents, vtimezone.component.jCal],
   ];

Apply this fix to all vcalendar constructions in the codebase.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const newJCal = [
"vcalendar",
[],
[...finalVevents, vtimezone.component.jCal],
];
const newJCal = [
"vcalendar",
[
["version", {}, "text", "2.0"],
["prodid", {}, "text", "-//Twake//Calendar//EN"],
],
[...finalVevents, vtimezone.component.jCal],
];
🤖 Prompt for AI Agents
In `@src/features/Events/api/updateSeries.ts` around lines 250 - 254, The
vcalendar jCal is being created with an empty properties array (e.g. the newJCal
in updateSeries.ts) which drops required VERSION and PRODID; update the
construction to copy or synthesize the VERSION and PRODID properties from the
incoming calendar's vcalendar properties (e.g. read from the original vcalendar
component/properties alongside vtimezone and finalVevents) and include them in
the properties array instead of leaving it empty. Apply the same change where
vcalendar jCal objects are built elsewhere (references: eventUtils.ts,
EventApi.ts occurrences and updateSeries.ts newJCal) so each constructed
vcalendar includes the VERSION and PRODID entries taken from the source calendar
data.

@Eriikah Eriikah merged commit fad1dfe into main Feb 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recurring event: only date changes should reset recurring event exceptions

2 participants