Add Google calendar API and workspace sync

This commit is contained in:
zsolt
2026-08-13 00:50:47 +02:00
parent 164f7ccad7
commit ab6ce48a83
11 changed files with 1119 additions and 677 deletions
+84 -51
View File
@@ -219,6 +219,7 @@ class JarvisSettings {
JarvisSettings({
required this.apiUrl,
required this.apiToken,
this.workspaceToken,
this.deviceName,
this.deviceId,
this.deviceToken,
@@ -227,6 +228,7 @@ class JarvisSettings {
final String apiUrl;
final String apiToken;
final String? workspaceToken;
final String? deviceName;
final int? deviceId;
final String? deviceToken;
@@ -240,6 +242,7 @@ class JarvisSettings {
? defaultJarvisApiUrl
: json['apiUrl'] as String? ?? JarvisSettings.defaults().apiUrl,
apiToken: json['apiToken'] as String? ?? '',
workspaceToken: json['workspaceToken'] as String?,
deviceName: json['deviceName'] as String?,
deviceId: json['deviceId'] as int?,
deviceToken: json['deviceToken'] as String?,
@@ -250,6 +253,7 @@ class JarvisSettings {
JarvisSettings copyWith({
String? apiUrl,
String? apiToken,
String? workspaceToken,
String? deviceName,
int? deviceId,
String? deviceToken,
@@ -259,6 +263,7 @@ class JarvisSettings {
return JarvisSettings(
apiUrl: apiUrl ?? this.apiUrl,
apiToken: apiToken ?? this.apiToken,
workspaceToken: workspaceToken ?? this.workspaceToken,
deviceName: clearDevice ? null : deviceName ?? this.deviceName,
deviceId: clearDevice ? null : deviceId ?? this.deviceId,
deviceToken: clearDevice ? null : deviceToken ?? this.deviceToken,
@@ -271,6 +276,7 @@ class JarvisSettings {
Map<String, dynamic> toJson() => {
'apiUrl': apiUrl,
'apiToken': apiToken,
'workspaceToken': workspaceToken,
'deviceName': deviceName,
'deviceId': deviceId,
'deviceToken': deviceToken,
@@ -335,49 +341,40 @@ class GoogleCalendarConnection {
};
}
class GoogleCalendarOption {
const GoogleCalendarOption({
class GoogleCalendarInfo {
const GoogleCalendarInfo({
required this.id,
required this.name,
required this.description,
required this.color,
this.description = '',
this.primary = false,
});
final String id;
final String name;
final String description;
final Color color;
}
final bool primary;
const availableGoogleCalendars = [
GoogleCalendarOption(
id: 'primary',
name: 'Saj\u00e1t napt\u00e1r',
description: 'A f\u0151 Google Calendar esem\u00e9nyeid.',
color: Color(0xFF4285F4),
),
GoogleCalendarOption(
id: 'family',
name: 'Csal\u00e1di napt\u00e1r',
description:
'K\u00f6z\u00f6s csal\u00e1di programok \u00e9s eml\u00e9keztet\u0151k.',
color: Color(0xFF34A853),
),
GoogleCalendarOption(
id: 'school',
name: 'Ovi / iskola',
description:
'Iskolai, \u00f3vodai \u00e9s k\u00fcl\u00f6n\u00f3r\u00e1s esem\u00e9nyek.',
color: Color(0xFFFBBC05),
),
GoogleCalendarOption(
id: 'work',
name: 'Munka',
description:
'Munkahelyi id\u0151pontok, ha szeretn\u00e9d l\u00e1tni \u0151ket.',
color: Color(0xFF9B78B4),
),
];
factory GoogleCalendarInfo.fromJson(Map<String, dynamic> json) {
final backgroundColor = json['backgroundColor'] as String?;
return GoogleCalendarInfo(
id: json['id'] as String? ?? 'primary',
name: json['summaryOverride'] as String? ??
json['summary'] as String? ??
'N\u00e9vtelen Google napt\u00e1r',
description: [
json['description'] as String?,
json['accessRole'] as String?,
]
.whereType<String>()
.where((part) => part.trim().isNotEmpty)
.join(' \u00b7 '),
color: parseGoogleColor(backgroundColor),
primary: json['primary'] == true,
);
}
}
List<GoogleCalendarConnection> defaultGoogleConnections() => [];
@@ -472,7 +469,7 @@ class CalendarEvent {
final List<String> todos;
final EventSource source;
bool get isSynced => source == EventSource.deviceCalendar;
bool get isSynced => source == EventSource.googleCalendar;
bool get isUncategorized => personId == null;
bool get isForEveryone => personId == everyonePersonId;
@@ -531,31 +528,37 @@ class CalendarEvent {
);
}
factory CalendarEvent.fromDeviceEvent(
device_calendar.Event event,
device_calendar.Calendar calendar, {
factory CalendarEvent.fromGoogleEvent(
Map<String, dynamic> event,
GoogleCalendarInfo calendar, {
String? personId,
}) {
final startDate = event.start?.toLocal() ?? DateTime.now();
final endDate = event.end?.toLocal();
final startJson = event['start'] as Map<String, dynamic>? ?? const {};
final endJson = event['end'] as Map<String, dynamic>? ?? const {};
final startDate = parseGoogleEventDate(startJson) ?? DateTime.now();
final endDate = parseGoogleEventDate(endJson);
final allDay = startJson['date'] != null;
final title = event['summary'] as String?;
final location = event['location'] as String?;
final description = event['description'] as String?;
return CalendarEvent(
event.title?.trim().isNotEmpty == true
? event.title!.trim()
: 'N\u00e9vtelen napt\u00e1resem\u00e9ny',
event.allDay == true ? 'Eg\u00e9sz nap' : formatTime(startDate),
event.allDay == true || endDate == null ? '' : formatTime(endDate),
Color(calendar.color ?? const Color(0xFF4285F4).toARGB32()),
title?.trim().isNotEmpty == true
? title!.trim()
: 'N\u00e9vtelen Google esem\u00e9ny',
allDay ? 'Eg\u00e9sz nap' : formatTime(startDate),
allDay || endDate == null ? '' : formatTime(endDate),
calendar.color,
date: DateTime(startDate.year, startDate.month, startDate.day),
place: [
calendar.name ?? 'Napt\u00e1r',
if ((event.location ?? '').trim().isNotEmpty) event.location!.trim(),
calendar.name,
if ((location ?? '').trim().isNotEmpty) location!.trim(),
].join(' \u00b7 '),
externalId: event.eventId,
externalId: event['id'] as String?,
calendarId: calendar.id,
calendarName: calendar.name,
personId: personId,
note: event.description ?? '',
source: EventSource.deviceCalendar,
note: description ?? '',
source: EventSource.googleCalendar,
);
}
@@ -577,7 +580,7 @@ class CalendarEvent {
};
}
enum EventSource { local, deviceCalendar }
enum EventSource { local, googleCalendar }
class ShoppingItem {
ShoppingItem(this.name, this.amount, this.category, {this.done = false});
@@ -586,6 +589,20 @@ class ShoppingItem {
final String amount;
final String category;
bool done;
factory ShoppingItem.fromJson(Map<String, dynamic> json) => ShoppingItem(
json['name'] as String? ?? '',
json['amount'] as String? ?? '1 db',
json['category'] as String? ?? 'Egy\u00e9b',
done: json['done'] as bool? ?? false,
);
Map<String, dynamic> toJson() => {
'name': name,
'amount': amount,
'category': category,
'done': done,
};
}
class StockItem {
@@ -596,6 +613,22 @@ class StockItem {
String unit;
String location;
String expiry;
factory StockItem.fromJson(Map<String, dynamic> json) => StockItem(
json['name'] as String? ?? '',
json['amount'] as int? ?? 1,
json['unit'] as String? ?? 'db',
json['location'] as String? ?? 'Kamra',
json['expiry'] as String? ?? 'nincs megadva',
);
Map<String, dynamic> toJson() => {
'name': name,
'amount': amount,
'unit': unit,
'location': location,
'expiry': expiry,
};
}
class JarvisProduct {