From 6975055fd388c813e730a9be65f6fc96ad5f213e Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Sat, 6 Aug 2022 07:24:13 -0700 Subject: Support direct calendar creation --- modern/src/settings/CalendarPage.js | 132 +++++++++++++++++++++++++++++++++--- web/l10n/en.json | 14 ++++ 2 files changed, 137 insertions(+), 9 deletions(-) diff --git a/modern/src/settings/CalendarPage.js b/modern/src/settings/CalendarPage.js index 27fdb462..862aa9a3 100644 --- a/modern/src/settings/CalendarPage.js +++ b/modern/src/settings/CalendarPage.js @@ -1,7 +1,8 @@ +import moment from 'moment'; import React, { useState } from 'react'; import TextField from '@mui/material/TextField'; import { - Accordion, AccordionSummary, AccordionDetails, Typography, + Accordion, AccordionSummary, AccordionDetails, Typography, FormControl, InputLabel, Select, MenuItem, } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; @@ -10,6 +11,49 @@ import EditItemView from './components/EditItemView'; import EditAttributesAccordion from './components/EditAttributesAccordion'; import { useTranslation } from '../common/components/LocalizationProvider'; import SettingsMenu from './components/SettingsMenu'; +import { prefixString } from '../common/util/stringUtils'; + +const formatTime = (time) => { + const tzid = Intl.DateTimeFormat().resolvedOptions().timeZone; + return `TZID=${tzid}:${time.format('YYYYMMDDTHHmmss')}`; +}; + +const parseRule = (rule) => { + const fragments = rule.split(';'); + const frequency = fragments[0].includes('FREQ') ? fragments[0].substring(11) : 'ONCE'; + const by = fragments.length > 1 ? fragments[1].split('=')[1].split(',') : null; + return { frequency, by }; +}; + +const formatRule = (rule) => { + const by = rule.by && rule.by.join(','); + switch (rule.frequency) { + case 'DAILY': + return `RRULE:FREQ=${rule.frequency}`; + case 'WEEKLY': + return `RRULE:FREQ=${rule.frequency};BYDAY=${by || 'SU'}`; + case 'MONTHLY': + return `RRULE:FREQ=${rule.frequency};BYMONTHDAY=${by || 1}`; + default: + return 'RRULE:'; + } +}; + +const updateCalendar = (lines, index, element) => window.btoa(lines.map((e, i) => (i !== index ? e : element)).join('\n')); + +const simpleCalendar = () => window.btoa([ + 'BEGIN:VCALENDAR', + 'VERSION:2.0', + 'PRODID:-//Traccar//NONSGML Traccar//EN', + 'BEGIN:VEVENT', + 'UID:00000000-0000-0000-0000-000000000000', + `DTSTART;${formatTime(moment())}`, + `DTEND;${formatTime(moment().add(1, 'hours'))}`, + 'RRULE:FREQ=DAILY', + 'SUMMARY:Event', + 'END:VEVENT', + 'END:VCALENDAR', +].join('\n')); const useStyles = makeStyles((theme) => ({ details: { @@ -26,6 +70,14 @@ const CalendarPage = () => { const [item, setItem] = useState(); + const decoded = item && item.data && window.atob(item.data); + + const simple = decoded && decoded.indexOf('//Traccar//') > 0; + + const lines = decoded && decoded.split('\n'); + + const rule = simple && parseRule(lines[7]); + const handleFiles = (files) => { if (files.length > 0) { const reader = new FileReader(); @@ -34,8 +86,6 @@ const CalendarPage = () => { setItem({ ...item, data: result.substr(result.indexOf(',') + 1) }); }; reader.readAsDataURL(files[0]); - } else { - setItem({ ...item, data: null }); } }; @@ -46,6 +96,7 @@ const CalendarPage = () => { endpoint="calendars" item={item} setItem={setItem} + defaultItem={{ data: simpleCalendar() }} validate={validate} menu={} breadcrumbs={['settingsTitle', 'sharedCalendar']} @@ -64,12 +115,75 @@ const CalendarPage = () => { onChange={(event) => setItem({ ...item, name: event.target.value })} label={t('sharedName')} /> - + + {t('sharedType')} + + + {simple ? ( + <> + { + const time = formatTime(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL)); + setItem({ ...item, data: updateCalendar(lines, 5, `DTSTART;${time}`) }); + }} + /> + { + const time = formatTime(moment(e.target.value, moment.HTML5_FMT.DATETIME_LOCAL)); + setItem({ ...item, data: updateCalendar(lines, 6, `DTEND;${time}`) }); + }} + /> + + {t('calendarRecurrence')} + + + {['WEEKLY', 'MONTHLY'].includes(rule.frequency) && ( + + {t('calendarDays')} + + + )} + + ) : ( + + )}