mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-15 23:55:21 -04:00
59998e9fd2
- Add keyboard navigation to overview - Add edit events to overview - Add create events to overview - Add setting for auto/khal/dankcalendar backend selection
351 lines
12 KiB
QML
351 lines
12 KiB
QML
import QtQuick
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
Item {
|
|
id: root
|
|
|
|
LayoutMirroring.enabled: I18n.isRtl
|
|
LayoutMirroring.childrenInherit: true
|
|
|
|
property var eventData: null
|
|
property date initialDate: new Date()
|
|
|
|
signal saved
|
|
signal closeRequested
|
|
|
|
property string fTitle: ""
|
|
property bool fAllDay: false
|
|
property date fDate: initialDate
|
|
property string fStart: "10:00"
|
|
property string fEnd: "11:00"
|
|
property string fLocation: ""
|
|
property string fDescription: ""
|
|
property string fCalendarId: ""
|
|
property int fReminder: -1
|
|
property string errorText: ""
|
|
property bool saving: false
|
|
|
|
readonly property var _cals: CalendarService.writableCalendars()
|
|
readonly property var _remLabels: [I18n.tr("No reminder"), I18n.tr("At start"), I18n.tr("5 min before"), I18n.tr("10 min before"), I18n.tr("15 min before"), I18n.tr("30 min before"), I18n.tr("1 hour before"), I18n.tr("1 day before")]
|
|
readonly property var _remMins: [-1, 0, 5, 10, 15, 30, 60, 1440]
|
|
|
|
function _parseTime(value) {
|
|
const m = value.trim().match(/^(\d{1,2}):(\d{2})$/);
|
|
if (!m)
|
|
return null;
|
|
const h = parseInt(m[1]);
|
|
const min = parseInt(m[2]);
|
|
if (h > 23 || min > 59)
|
|
return null;
|
|
return {
|
|
"h": h,
|
|
"m": min
|
|
};
|
|
}
|
|
|
|
function _isoFromDateTime(dateObj, h, m) {
|
|
const d = new Date(dateObj);
|
|
d.setHours(h, m, 0, 0);
|
|
return d.toISOString();
|
|
}
|
|
|
|
function _allDayIso(dateObj, dayOffset) {
|
|
return new Date(Date.UTC(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate() + dayOffset)).toISOString();
|
|
}
|
|
|
|
function _calendarName(id) {
|
|
for (let i = 0; i < _cals.length; i++) {
|
|
if (_cals[i].id === id)
|
|
return _cals[i].name;
|
|
}
|
|
return _cals.length > 0 ? _cals[0].name : "";
|
|
}
|
|
|
|
function save() {
|
|
const title = fTitle.trim();
|
|
if (!title) {
|
|
errorText = I18n.tr("Title is required");
|
|
return;
|
|
}
|
|
let calId = fCalendarId;
|
|
if (!calId) {
|
|
const def = CalendarService.defaultCalendar();
|
|
calId = def ? def.id : "";
|
|
}
|
|
if (!calId) {
|
|
errorText = I18n.tr("No writable calendar available");
|
|
return;
|
|
}
|
|
let startIso, endIso;
|
|
if (fAllDay) {
|
|
startIso = _allDayIso(fDate, 0);
|
|
endIso = _allDayIso(fDate, 1);
|
|
} else {
|
|
const s = _parseTime(fStart);
|
|
const e = _parseTime(fEnd);
|
|
if (!s || !e) {
|
|
errorText = I18n.tr("Use HH:MM time format");
|
|
return;
|
|
}
|
|
startIso = _isoFromDateTime(fDate, s.h, s.m);
|
|
endIso = _isoFromDateTime(fDate, e.h, e.m);
|
|
if (new Date(endIso).getTime() <= new Date(startIso).getTime()) {
|
|
errorText = I18n.tr("End must be after start");
|
|
return;
|
|
}
|
|
}
|
|
const fields = {
|
|
"calendarId": calId,
|
|
"summary": title,
|
|
"description": fDescription,
|
|
"location": fLocation,
|
|
"start": startIso,
|
|
"end": endIso,
|
|
"allDay": fAllDay,
|
|
"reminders": fReminder >= 0 ? [
|
|
{
|
|
"method": "popup",
|
|
"minutes": fReminder
|
|
}
|
|
] : []
|
|
};
|
|
saving = true;
|
|
errorText = "";
|
|
const cb = response => {
|
|
saving = false;
|
|
if (response.error) {
|
|
errorText = response.error;
|
|
return;
|
|
}
|
|
root.saved();
|
|
};
|
|
if (eventData && eventData.id)
|
|
CalendarService.updateEvent(eventData.id, fields, cb);
|
|
else
|
|
CalendarService.createEvent(fields, cb);
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
if (!eventData) {
|
|
fCalendarId = CalendarService.defaultCalendar() ? CalendarService.defaultCalendar().id : "";
|
|
return;
|
|
}
|
|
fTitle = eventData.title || "";
|
|
fAllDay = !!eventData.allDay;
|
|
fDate = eventData.start;
|
|
const fmt = "HH:mm";
|
|
fStart = Qt.formatTime(eventData.start, fmt);
|
|
fEnd = Qt.formatTime(eventData.end, fmt);
|
|
fLocation = eventData.location || "";
|
|
fDescription = eventData.description || "";
|
|
fCalendarId = eventData.calendarId || "";
|
|
if (eventData.reminders && eventData.reminders.length > 0)
|
|
fReminder = eventData.reminders[0].minutes;
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: Theme.cornerRadius
|
|
color: Qt.rgba(0, 0, 0, 0.45)
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: root.closeRequested()
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.centerIn: parent
|
|
width: Math.min(parent.width - Theme.spacingL * 2, 400)
|
|
height: Math.min(parent.height - Theme.spacingM, 300)
|
|
radius: Theme.cornerRadius
|
|
color: Theme.surfaceContainerHigh
|
|
border.color: Theme.outlineMedium
|
|
border.width: 1
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
}
|
|
|
|
DankFlickable {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingM
|
|
contentWidth: width
|
|
contentHeight: form.implicitHeight
|
|
clip: true
|
|
|
|
Column {
|
|
id: form
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
text: root.eventData ? I18n.tr("Edit event") : I18n.tr("New event")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
DankTextField {
|
|
width: parent.width
|
|
labelText: I18n.tr("Title")
|
|
leftIconName: "title"
|
|
leftIconSize: Theme.iconSize - 6
|
|
placeholderText: I18n.tr("Event title")
|
|
text: root.fTitle
|
|
onTextChanged: root.fTitle = text
|
|
}
|
|
|
|
DankToggle {
|
|
width: parent.width
|
|
text: I18n.tr("All day")
|
|
checked: root.fAllDay
|
|
onToggled: checked => root.fAllDay = checked
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingXS
|
|
|
|
DankActionButton {
|
|
circular: false
|
|
iconName: "chevron_left"
|
|
iconSize: 16
|
|
onClicked: {
|
|
let d = new Date(root.fDate);
|
|
d.setDate(d.getDate() - 1);
|
|
root.fDate = d;
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
width: parent.width - 72
|
|
text: Qt.formatDate(root.fDate, "ddd, MMM d yyyy")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceText
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
height: 32
|
|
}
|
|
|
|
DankActionButton {
|
|
circular: false
|
|
iconName: "chevron_right"
|
|
iconSize: 16
|
|
onClicked: {
|
|
let d = new Date(root.fDate);
|
|
d.setDate(d.getDate() + 1);
|
|
root.fDate = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
visible: !root.fAllDay
|
|
|
|
DankTextField {
|
|
width: (parent.width - Theme.spacingS) / 2
|
|
labelText: I18n.tr("Start")
|
|
leftIconName: "schedule"
|
|
leftIconSize: Theme.iconSize - 6
|
|
placeholderText: "HH:MM"
|
|
text: root.fStart
|
|
onTextChanged: root.fStart = text
|
|
}
|
|
|
|
DankTextField {
|
|
width: (parent.width - Theme.spacingS) / 2
|
|
labelText: I18n.tr("End")
|
|
placeholderText: "HH:MM"
|
|
text: root.fEnd
|
|
onTextChanged: root.fEnd = text
|
|
}
|
|
}
|
|
|
|
DankDropdown {
|
|
width: parent.width
|
|
text: I18n.tr("Calendar")
|
|
options: root._cals.map(c => c.name)
|
|
currentValue: root._calendarName(root.fCalendarId)
|
|
onValueChanged: value => {
|
|
for (let i = 0; i < root._cals.length; i++) {
|
|
if (root._cals[i].name === value) {
|
|
root.fCalendarId = root._cals[i].id;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DankDropdown {
|
|
width: parent.width
|
|
text: I18n.tr("Reminder")
|
|
options: root._remLabels
|
|
currentValue: root._remLabels[Math.max(0, root._remMins.indexOf(root.fReminder))]
|
|
onValueChanged: value => {
|
|
const idx = root._remLabels.indexOf(value);
|
|
if (idx >= 0)
|
|
root.fReminder = root._remMins[idx];
|
|
}
|
|
}
|
|
|
|
DankTextField {
|
|
width: parent.width
|
|
labelText: I18n.tr("Location")
|
|
leftIconName: "place"
|
|
leftIconSize: Theme.iconSize - 6
|
|
placeholderText: I18n.tr("Add location")
|
|
text: root.fLocation
|
|
onTextChanged: root.fLocation = text
|
|
}
|
|
|
|
DankTextField {
|
|
width: parent.width
|
|
labelText: I18n.tr("Notes")
|
|
leftIconName: "notes"
|
|
leftIconSize: Theme.iconSize - 6
|
|
placeholderText: I18n.tr("Add notes")
|
|
text: root.fDescription
|
|
onTextChanged: root.fDescription = text
|
|
}
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
text: root.errorText
|
|
visible: root.errorText !== ""
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.error
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
DankButton {
|
|
text: root.saving ? I18n.tr("Saving…") : I18n.tr("Save")
|
|
iconName: "check"
|
|
buttonHeight: 32
|
|
backgroundColor: Theme.primary
|
|
textColor: Theme.primaryText
|
|
enabled: !root.saving
|
|
onClicked: root.save()
|
|
}
|
|
|
|
DankButton {
|
|
text: I18n.tr("Cancel")
|
|
buttonHeight: 32
|
|
onClicked: root.closeRequested()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|