|
1 | 1 | import { File, Paths } from 'expo-file-system'; |
2 | 2 | import ICAL from 'ical.js'; |
3 | | -import type { CalendarEvent, NextEvent, WidgetClass } from '../types'; |
| 3 | +import type { CalendarEvent, NextClassWidgetProps, NextEvent, WidgetClass } from '../types'; |
4 | 4 | import { stringToColour } from '../utils/color'; |
5 | 5 |
|
6 | 6 | interface ICALComponent { |
@@ -142,6 +142,41 @@ export class EDT { |
142 | 142 | return { summary: next.summary, location: next.location }; |
143 | 143 | } |
144 | 144 |
|
| 145 | + async getNextEvent(adeid: string): Promise<NextEvent> { |
| 146 | + const icalData = await this.fetchEDT(adeid); |
| 147 | + if (!icalData) { |
| 148 | + return { summary: 'ADE Indisponible', location: 'Impossible de récupérer le cours.' }; |
| 149 | + } |
| 150 | + const events = this.parseICal(icalData); |
| 151 | + return this.findNextEvent(events); |
| 152 | + } |
| 153 | + |
| 154 | + private convertToCalendarEvents(events: ICALEvent[]): CalendarEvent[] { |
| 155 | + return events.map((e, index) => ({ |
| 156 | + id: e.uid ?? String(index), |
| 157 | + start: { dateTime: e.startDate.toJSDate().toISOString() }, |
| 158 | + end: { dateTime: e.endDate.toJSDate().toISOString() }, |
| 159 | + title: e.summary ?? '', |
| 160 | + subtitle: e.description ?? '', |
| 161 | + description: e.location ?? '', |
| 162 | + color: stringToColour(e.summary ?? ''), |
| 163 | + })); |
| 164 | + } |
| 165 | + |
| 166 | + async getEDT(adeid: string): Promise<CalendarEvent[]> { |
| 167 | + if (!adeid || adeid === 'demo') return []; |
| 168 | + |
| 169 | + const icalData = await this.fetchEDT(adeid); |
| 170 | + if (!icalData) return getCalendarFromCache(); |
| 171 | + const events = this.parseICal(icalData); |
| 172 | + const calEvents = this.convertToCalendarEvents(events); |
| 173 | + try { |
| 174 | + CALENDAR_FILE.write(JSON.stringify(calEvents)); |
| 175 | + } catch { } |
| 176 | + return calEvents; |
| 177 | + } |
| 178 | + |
| 179 | + // Widget |
145 | 180 | findNextTwoCourses(events: ICALEvent[]): WidgetClass[] { |
146 | 181 | const now = new Date(); |
147 | 182 | const windowStart = new Date(now.getTime() - 15 * 60 * 1000); |
@@ -174,36 +209,61 @@ export class EDT { |
174 | 209 | return this.findNextTwoCourses(events); |
175 | 210 | } |
176 | 211 |
|
177 | | - async getNextEvent(adeid: string): Promise<NextEvent> { |
178 | | - const icalData = await this.fetchEDT(adeid); |
179 | | - if (!icalData) { |
180 | | - return { summary: 'ADE Indisponible', location: 'Impossible de récupérer le cours.' }; |
| 212 | + buildWidgetTimeline( |
| 213 | + events: CalendarEvent[], |
| 214 | + ): Array<{ date: Date; props: NextClassWidgetProps }> { |
| 215 | + if (events.length === 0) { |
| 216 | + return [ |
| 217 | + { |
| 218 | + date: new Date(), |
| 219 | + props: { courses: [], configured: false }, |
| 220 | + }, |
| 221 | + ]; |
181 | 222 | } |
182 | | - const events = this.parseICal(icalData); |
183 | | - return this.findNextEvent(events); |
184 | | - } |
185 | 223 |
|
186 | | - private convertToCalendarEvents(events: ICALEvent[]): CalendarEvent[] { |
187 | | - return events.map((e, index) => ({ |
188 | | - id: e.uid ?? String(index), |
189 | | - start: { dateTime: e.startDate.toJSDate().toISOString() }, |
190 | | - end: { dateTime: e.endDate.toJSDate().toISOString() }, |
191 | | - title: e.summary ?? '', |
192 | | - subtitle: e.description ?? '', |
193 | | - description: e.location ?? '', |
194 | | - color: stringToColour(e.summary ?? ''), |
195 | | - })); |
196 | | - } |
| 224 | + const now = new Date(); |
| 225 | + const cutoff = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); // 7 jours |
197 | 226 |
|
198 | | - async getEDT(adeid: string): Promise<CalendarEvent[]> { |
199 | | - const icalData = await this.fetchEDT(adeid); |
200 | | - if (!icalData) return getCalendarFromCache(); |
201 | | - const events = this.parseICal(icalData); |
202 | | - const calEvents = this.convertToCalendarEvents(events); |
203 | | - try { |
204 | | - CALENDAR_FILE.write(JSON.stringify(calEvents)); |
205 | | - } catch { } |
206 | | - return calEvents; |
| 227 | + const pad = (n: number) => String(n).padStart(2, '0'); |
| 228 | + const fmt = (d: Date) => `${pad(d.getHours())}:${pad(d.getMinutes())}`; |
| 229 | + |
| 230 | + // Tous les cours futurs triés par heure de début |
| 231 | + const allFuture = events |
| 232 | + .filter((e) => new Date(e.end.dateTime) > now) |
| 233 | + .sort( |
| 234 | + (a, b) => |
| 235 | + new Date(a.start.dateTime).getTime() - new Date(b.start.dateTime).getTime(), |
| 236 | + ); |
| 237 | + |
| 238 | + // Les 2 prochains cours non encore terminés à partir d'un instant donné |
| 239 | + const getCoursesAt = (from: Date): WidgetClass[] => |
| 240 | + allFuture |
| 241 | + .filter((e) => new Date(e.end.dateTime) > from) |
| 242 | + .slice(0, 2) |
| 243 | + .map((e) => ({ |
| 244 | + title: e.title, |
| 245 | + room: e.description, // description = location dans CalendarEvent |
| 246 | + startTime: fmt(new Date(e.start.dateTime)), |
| 247 | + endTime: fmt(new Date(e.end.dateTime)), |
| 248 | + })); |
| 249 | + |
| 250 | + const seen = new Set<number>(); |
| 251 | + const timeline: Array<{ date: Date; props: NextClassWidgetProps }> = []; |
| 252 | + |
| 253 | + seen.add(now.getTime()); |
| 254 | + timeline.push({ date: now, props: { courses: getCoursesAt(now), configured: true } }); |
| 255 | + |
| 256 | + // Une entrée à chaque fin de cours dans les 24h |
| 257 | + for (const event of allFuture) { |
| 258 | + const endDate = new Date(event.end.dateTime); |
| 259 | + const ms = endDate.getTime(); |
| 260 | + if (endDate <= cutoff && !seen.has(ms)) { |
| 261 | + seen.add(ms); |
| 262 | + timeline.push({ date: endDate, props: { courses: getCoursesAt(endDate), configured: true } }); |
| 263 | + } |
| 264 | + } |
| 265 | + |
| 266 | + return timeline; |
207 | 267 | } |
208 | 268 | } |
209 | 269 |
|
|
0 commit comments