Persist per-event Google calendar assignments
This commit is contained in:
+18
-5
@@ -780,7 +780,9 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
|
|
||||||
setState(() => workspaceSyncing = true);
|
setState(() => workspaceSyncing = true);
|
||||||
try {
|
try {
|
||||||
final baseUri = Uri.parse(normalizeJarvisUrl(widget.jarvisSettings.apiUrl));
|
final baseUri = Uri.parse(
|
||||||
|
normalizeJarvisUrl(widget.jarvisSettings.apiUrl),
|
||||||
|
);
|
||||||
final response = await http
|
final response = await http
|
||||||
.get(
|
.get(
|
||||||
baseUri.resolve('/api/workspace/state'),
|
baseUri.resolve('/api/workspace/state'),
|
||||||
@@ -847,7 +849,9 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
|
|
||||||
setState(() => workspaceSyncing = true);
|
setState(() => workspaceSyncing = true);
|
||||||
try {
|
try {
|
||||||
final baseUri = Uri.parse(normalizeJarvisUrl(widget.jarvisSettings.apiUrl));
|
final baseUri = Uri.parse(
|
||||||
|
normalizeJarvisUrl(widget.jarvisSettings.apiUrl),
|
||||||
|
);
|
||||||
final stateJson = jsonEncode({
|
final stateJson = jsonEncode({
|
||||||
'version': 1,
|
'version': 1,
|
||||||
'updatedAt': DateTime.now().toIso8601String(),
|
'updatedAt': DateTime.now().toIso8601String(),
|
||||||
@@ -879,9 +883,9 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
|
|
||||||
void _showWorkspaceMessage(String message) {
|
void _showWorkspaceMessage(String message) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(
|
||||||
SnackBar(content: Text(message)),
|
context,
|
||||||
);
|
).showSnackBar(SnackBar(content: Text(message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<JarvisSettings> _registerJarvisDevice(
|
Future<JarvisSettings> _registerJarvisDevice(
|
||||||
@@ -1133,6 +1137,15 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _assignSyncedEventToPerson(CalendarEvent event, Person person) {
|
void _assignSyncedEventToPerson(CalendarEvent event, Person person) {
|
||||||
|
if (widget.googleConnections.isNotEmpty) {
|
||||||
|
final currentConnection = widget.googleConnections.first;
|
||||||
|
final nextEventPersonIds = {...currentConnection.eventPersonIds};
|
||||||
|
nextEventPersonIds[importedEventKey(event)] = person.id;
|
||||||
|
widget.onGoogleConnectionsChanged([
|
||||||
|
currentConnection.copyWith(eventPersonIds: nextEventPersonIds),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
final nextSyncedEvents = widget.syncedEvents
|
final nextSyncedEvents = widget.syncedEvents
|
||||||
.map(
|
.map(
|
||||||
(item) => sameImportedEvent(item, event)
|
(item) => sameImportedEvent(item, event)
|
||||||
|
|||||||
+15
-5
@@ -310,6 +310,7 @@ class GoogleCalendarConnection {
|
|||||||
required this.accountEmail,
|
required this.accountEmail,
|
||||||
required this.enabledCalendarIds,
|
required this.enabledCalendarIds,
|
||||||
this.calendarPersonIds = const {},
|
this.calendarPersonIds = const {},
|
||||||
|
this.eventPersonIds = const {},
|
||||||
required this.lastSync,
|
required this.lastSync,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -317,12 +318,14 @@ class GoogleCalendarConnection {
|
|||||||
final String accountEmail;
|
final String accountEmail;
|
||||||
final List<String> enabledCalendarIds;
|
final List<String> enabledCalendarIds;
|
||||||
final Map<String, String> calendarPersonIds;
|
final Map<String, String> calendarPersonIds;
|
||||||
|
final Map<String, String> eventPersonIds;
|
||||||
final DateTime? lastSync;
|
final DateTime? lastSync;
|
||||||
|
|
||||||
GoogleCalendarConnection copyWith({
|
GoogleCalendarConnection copyWith({
|
||||||
String? accountEmail,
|
String? accountEmail,
|
||||||
List<String>? enabledCalendarIds,
|
List<String>? enabledCalendarIds,
|
||||||
Map<String, String>? calendarPersonIds,
|
Map<String, String>? calendarPersonIds,
|
||||||
|
Map<String, String>? eventPersonIds,
|
||||||
DateTime? lastSync,
|
DateTime? lastSync,
|
||||||
}) {
|
}) {
|
||||||
return GoogleCalendarConnection(
|
return GoogleCalendarConnection(
|
||||||
@@ -330,6 +333,7 @@ class GoogleCalendarConnection {
|
|||||||
accountEmail: accountEmail ?? this.accountEmail,
|
accountEmail: accountEmail ?? this.accountEmail,
|
||||||
enabledCalendarIds: enabledCalendarIds ?? this.enabledCalendarIds,
|
enabledCalendarIds: enabledCalendarIds ?? this.enabledCalendarIds,
|
||||||
calendarPersonIds: calendarPersonIds ?? this.calendarPersonIds,
|
calendarPersonIds: calendarPersonIds ?? this.calendarPersonIds,
|
||||||
|
eventPersonIds: eventPersonIds ?? this.eventPersonIds,
|
||||||
lastSync: lastSync ?? this.lastSync,
|
lastSync: lastSync ?? this.lastSync,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -337,6 +341,7 @@ class GoogleCalendarConnection {
|
|||||||
factory GoogleCalendarConnection.fromJson(Map<String, dynamic> json) {
|
factory GoogleCalendarConnection.fromJson(Map<String, dynamic> json) {
|
||||||
final rawCalendarIds = json['enabledCalendarIds'];
|
final rawCalendarIds = json['enabledCalendarIds'];
|
||||||
final rawCalendarPersonIds = json['calendarPersonIds'];
|
final rawCalendarPersonIds = json['calendarPersonIds'];
|
||||||
|
final rawEventPersonIds = json['eventPersonIds'];
|
||||||
return GoogleCalendarConnection(
|
return GoogleCalendarConnection(
|
||||||
id: json['id'] as String? ?? uniqueId(),
|
id: json['id'] as String? ?? uniqueId(),
|
||||||
accountEmail: json['accountEmail'] as String? ?? '',
|
accountEmail: json['accountEmail'] as String? ?? '',
|
||||||
@@ -348,6 +353,11 @@ class GoogleCalendarConnection {
|
|||||||
(key, value) => MapEntry(key.toString(), value.toString()),
|
(key, value) => MapEntry(key.toString(), value.toString()),
|
||||||
)
|
)
|
||||||
: const {},
|
: const {},
|
||||||
|
eventPersonIds: rawEventPersonIds is Map
|
||||||
|
? rawEventPersonIds.map(
|
||||||
|
(key, value) => MapEntry(key.toString(), value.toString()),
|
||||||
|
)
|
||||||
|
: const {},
|
||||||
lastSync: DateTime.tryParse(json['lastSync'] as String? ?? ''),
|
lastSync: DateTime.tryParse(json['lastSync'] as String? ?? ''),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -357,6 +367,7 @@ class GoogleCalendarConnection {
|
|||||||
'accountEmail': accountEmail,
|
'accountEmail': accountEmail,
|
||||||
'enabledCalendarIds': enabledCalendarIds,
|
'enabledCalendarIds': enabledCalendarIds,
|
||||||
'calendarPersonIds': calendarPersonIds,
|
'calendarPersonIds': calendarPersonIds,
|
||||||
|
'eventPersonIds': eventPersonIds,
|
||||||
'lastSync': lastSync?.toIso8601String(),
|
'lastSync': lastSync?.toIso8601String(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -380,13 +391,12 @@ class GoogleCalendarInfo {
|
|||||||
final backgroundColor = json['backgroundColor'] as String?;
|
final backgroundColor = json['backgroundColor'] as String?;
|
||||||
return GoogleCalendarInfo(
|
return GoogleCalendarInfo(
|
||||||
id: json['id'] as String? ?? 'primary',
|
id: json['id'] as String? ?? 'primary',
|
||||||
name: json['summaryOverride'] as String? ??
|
name:
|
||||||
|
json['summaryOverride'] as String? ??
|
||||||
json['summary'] as String? ??
|
json['summary'] as String? ??
|
||||||
'N\u00e9vtelen Google napt\u00e1r',
|
'N\u00e9vtelen Google napt\u00e1r',
|
||||||
description: [
|
description:
|
||||||
json['description'] as String?,
|
[json['description'] as String?, json['accessRole'] as String?]
|
||||||
json['accessRole'] as String?,
|
|
||||||
]
|
|
||||||
.whereType<String>()
|
.whereType<String>()
|
||||||
.where((part) => part.trim().isNotEmpty)
|
.where((part) => part.trim().isNotEmpty)
|
||||||
.join(' \u00b7 '),
|
.join(' \u00b7 '),
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ class AccountsPage extends StatefulWidget {
|
|||||||
State<AccountsPage> createState() => _AccountsPageState();
|
State<AccountsPage> createState() => _AccountsPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver {
|
class _AccountsPageState extends State<AccountsPage>
|
||||||
|
with WidgetsBindingObserver {
|
||||||
List<GoogleCalendarInfo> googleCalendars = [];
|
List<GoogleCalendarInfo> googleCalendars = [];
|
||||||
bool loadingCalendars = false;
|
bool loadingCalendars = false;
|
||||||
bool syncing = false;
|
bool syncing = false;
|
||||||
@@ -89,7 +90,10 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
|
|
||||||
Uri _jarvisUri(String path, [Map<String, String?> query = const {}]) {
|
Uri _jarvisUri(String path, [Map<String, String?> query = const {}]) {
|
||||||
final baseUri = Uri.parse(normalizeJarvisUrl(widget.jarvisSettings.apiUrl));
|
final baseUri = Uri.parse(normalizeJarvisUrl(widget.jarvisSettings.apiUrl));
|
||||||
return baseUri.replace(path: path, queryParameters: query.isEmpty ? null : query);
|
return baseUri.replace(
|
||||||
|
path: path,
|
||||||
|
queryParameters: query.isEmpty ? null : query,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _jarvisConfigError() {
|
String? _jarvisConfigError() {
|
||||||
@@ -121,7 +125,9 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
.get(_jarvisUri('/api/google/oauth/status'), headers: _jarvisHeaders)
|
.get(_jarvisUri('/api/google/oauth/status'), headers: _jarvisHeaders)
|
||||||
.timeout(const Duration(seconds: 10));
|
.timeout(const Duration(seconds: 10));
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('JARVIS status ${response.statusCode}: ${response.body}');
|
throw Exception(
|
||||||
|
'JARVIS status ${response.statusCode}: ${response.body}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final payload = jsonDecode(response.body);
|
final payload = jsonDecode(response.body);
|
||||||
if (payload is! Map<String, dynamic>) {
|
if (payload is! Map<String, dynamic>) {
|
||||||
@@ -137,6 +143,7 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
accountEmail: email ?? current?.accountEmail ?? 'Google fi\u00f3k',
|
accountEmail: email ?? current?.accountEmail ?? 'Google fi\u00f3k',
|
||||||
enabledCalendarIds: current?.enabledCalendarIds ?? const [],
|
enabledCalendarIds: current?.enabledCalendarIds ?? const [],
|
||||||
calendarPersonIds: current?.calendarPersonIds ?? const {},
|
calendarPersonIds: current?.calendarPersonIds ?? const {},
|
||||||
|
eventPersonIds: current?.eventPersonIds ?? const {},
|
||||||
lastSync: current?.lastSync,
|
lastSync: current?.lastSync,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
@@ -152,7 +159,8 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
loadingCalendars = false;
|
loadingCalendars = false;
|
||||||
statusMessage = 'JARVIS Google kapcsolat nem el\u00e9rhet\u0151: $error';
|
statusMessage =
|
||||||
|
'JARVIS Google kapcsolat nem el\u00e9rhet\u0151: $error';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +168,8 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
Future<void> _startGoogleConnection() async {
|
Future<void> _startGoogleConnection() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
loadingCalendars = true;
|
loadingCalendars = true;
|
||||||
statusMessage = 'Google \u00f6sszek\u00f6t\u00e9s ind\u00edt\u00e1sa JARVIS-on...';
|
statusMessage =
|
||||||
|
'Google \u00f6sszek\u00f6t\u00e9s ind\u00edt\u00e1sa JARVIS-on...';
|
||||||
});
|
});
|
||||||
|
|
||||||
final configError = _jarvisConfigError();
|
final configError = _jarvisConfigError();
|
||||||
@@ -176,17 +185,27 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
.get(_jarvisUri('/api/google/oauth/start'), headers: _jarvisHeaders)
|
.get(_jarvisUri('/api/google/oauth/start'), headers: _jarvisHeaders)
|
||||||
.timeout(const Duration(seconds: 10));
|
.timeout(const Duration(seconds: 10));
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('JARVIS OAuth ${response.statusCode}: ${response.body}');
|
throw Exception(
|
||||||
|
'JARVIS OAuth ${response.statusCode}: ${response.body}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
final payload = jsonDecode(response.body);
|
final payload = jsonDecode(response.body);
|
||||||
if (payload is! Map<String, dynamic>) throw Exception('Hib\u00e1s JARVIS v\u00e1lasz.');
|
if (payload is! Map<String, dynamic>) {
|
||||||
|
throw Exception('Hib\u00e1s JARVIS v\u00e1lasz.');
|
||||||
|
}
|
||||||
final url = payload['authorizationUrl'] as String?;
|
final url = payload['authorizationUrl'] as String?;
|
||||||
if (url == null || url.isEmpty) throw Exception('Hi\u00e1nyzik az OAuth URL.');
|
if (url == null || url.isEmpty) {
|
||||||
|
throw Exception('Hi\u00e1nyzik az OAuth URL.');
|
||||||
|
}
|
||||||
final launched = await launchUrl(
|
final launched = await launchUrl(
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
mode: LaunchMode.externalApplication,
|
mode: LaunchMode.externalApplication,
|
||||||
);
|
);
|
||||||
if (!launched) throw Exception('Nem siker\u00fclt megnyitni a b\u00f6ng\u00e9sz\u0151t.');
|
if (!launched) {
|
||||||
|
throw Exception(
|
||||||
|
'Nem siker\u00fclt megnyitni a b\u00f6ng\u00e9sz\u0151t.',
|
||||||
|
);
|
||||||
|
}
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
waitingForGoogleAuthReturn = true;
|
waitingForGoogleAuthReturn = true;
|
||||||
@@ -198,7 +217,8 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
loadingCalendars = false;
|
loadingCalendars = false;
|
||||||
statusMessage = 'Google \u00f6sszek\u00f6t\u00e9s nem indult el: $error';
|
statusMessage =
|
||||||
|
'Google \u00f6sszek\u00f6t\u00e9s nem indult el: $error';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,6 +263,7 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
accountEmail: 'Google Calendar',
|
accountEmail: 'Google Calendar',
|
||||||
enabledCalendarIds: const [],
|
enabledCalendarIds: const [],
|
||||||
calendarPersonIds: const {},
|
calendarPersonIds: const {},
|
||||||
|
eventPersonIds: const {},
|
||||||
lastSync: null,
|
lastSync: null,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
@@ -290,6 +311,17 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
.toList();
|
.toList();
|
||||||
final synced = <CalendarEvent>[];
|
final synced = <CalendarEvent>[];
|
||||||
final syncDetails = <String>[];
|
final syncDetails = <String>[];
|
||||||
|
final eventPersonOverrides = {...current.eventPersonIds};
|
||||||
|
for (final cachedEvent in widget.cachedEvents) {
|
||||||
|
if (!cachedEvent.isSynced || cachedEvent.personId == null) continue;
|
||||||
|
final defaultPersonId =
|
||||||
|
current.calendarPersonIds[cachedEvent.calendarId];
|
||||||
|
if (cachedEvent.personId == defaultPersonId) continue;
|
||||||
|
eventPersonOverrides.putIfAbsent(
|
||||||
|
importedEventKey(cachedEvent),
|
||||||
|
() => cachedEvent.personId!,
|
||||||
|
);
|
||||||
|
}
|
||||||
final now = DateTime.now();
|
final now = DateTime.now();
|
||||||
final start = DateTime(
|
final start = DateTime(
|
||||||
now.year,
|
now.year,
|
||||||
@@ -299,19 +331,18 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
final end = start.add(const Duration(days: 180));
|
final end = start.add(const Duration(days: 180));
|
||||||
|
|
||||||
for (final calendar in selectedCalendars) {
|
for (final calendar in selectedCalendars) {
|
||||||
final uri = _jarvisUri(
|
final uri = _jarvisUri('/api/google/events', {
|
||||||
'/api/google/events',
|
|
||||||
{
|
|
||||||
'calendarId': calendar.id,
|
'calendarId': calendar.id,
|
||||||
'timeMin': start.toUtc().toIso8601String(),
|
'timeMin': start.toUtc().toIso8601String(),
|
||||||
'timeMax': end.toUtc().toIso8601String(),
|
'timeMax': end.toUtc().toIso8601String(),
|
||||||
},
|
});
|
||||||
);
|
|
||||||
final response = await http
|
final response = await http
|
||||||
.get(uri, headers: _jarvisHeaders)
|
.get(uri, headers: _jarvisHeaders)
|
||||||
.timeout(const Duration(seconds: 20));
|
.timeout(const Duration(seconds: 20));
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('${calendar.name}: Events API ${response.statusCode}');
|
throw Exception(
|
||||||
|
'${calendar.name}: Events API ${response.statusCode}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final payload = jsonDecode(response.body);
|
final payload = jsonDecode(response.body);
|
||||||
@@ -323,13 +354,12 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
syncDetails.add('${calendar.name}: ${events.length}');
|
syncDetails.add('${calendar.name}: ${events.length}');
|
||||||
for (final event in events) {
|
for (final event in events) {
|
||||||
if (event['status'] == 'cancelled') continue;
|
if (event['status'] == 'cancelled') continue;
|
||||||
synced.add(
|
final imported = CalendarEvent.fromGoogleEvent(event, calendar);
|
||||||
CalendarEvent.fromGoogleEvent(
|
final eventKey = importedEventKey(imported);
|
||||||
event,
|
final assignedPersonId =
|
||||||
calendar,
|
eventPersonOverrides[eventKey] ??
|
||||||
personId: current.calendarPersonIds[calendar.id],
|
current.calendarPersonIds[calendar.id];
|
||||||
),
|
synced.add(imported.copyWith(personId: assignedPersonId));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +369,12 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
});
|
});
|
||||||
|
|
||||||
widget.onSyncedEventsChanged(synced);
|
widget.onSyncedEventsChanged(synced);
|
||||||
widget.onChanged([current.copyWith(lastSync: DateTime.now())]);
|
widget.onChanged([
|
||||||
|
current.copyWith(
|
||||||
|
eventPersonIds: eventPersonOverrides,
|
||||||
|
lastSync: DateTime.now(),
|
||||||
|
),
|
||||||
|
]);
|
||||||
setState(() {
|
setState(() {
|
||||||
syncing = false;
|
syncing = false;
|
||||||
statusMessage =
|
statusMessage =
|
||||||
@@ -369,9 +404,7 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
action: FilledButton.icon(
|
action: FilledButton.icon(
|
||||||
onPressed: loadingCalendars ? null : _startGoogleConnection,
|
onPressed: loadingCalendars ? null : _startGoogleConnection,
|
||||||
icon: const Icon(Icons.open_in_browser),
|
icon: const Icon(Icons.open_in_browser),
|
||||||
label: Text(
|
label: Text(hasConnection ? text.googleReconnect : text.googleConnect),
|
||||||
hasConnection ? text.googleReconnect : text.googleConnect,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
padding: const EdgeInsets.only(bottom: 24),
|
padding: const EdgeInsets.only(bottom: 24),
|
||||||
@@ -457,9 +490,7 @@ class _AccountsPageState extends State<AccountsPage> with WidgetsBindingObserver
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
OutlinedButton.icon(
|
OutlinedButton.icon(
|
||||||
onPressed: loadingCalendars
|
onPressed: loadingCalendars ? null : _loadGoogleConnectionStatus,
|
||||||
? null
|
|
||||||
: _loadGoogleConnectionStatus,
|
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
label: const Text('Google napt\u00e1rlista friss\u00edt\u00e9se'),
|
label: const Text('Google napt\u00e1rlista friss\u00edt\u00e9se'),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -86,6 +86,23 @@ Color parseGoogleColor(String? value) {
|
|||||||
|
|
||||||
String uniqueId() => DateTime.now().microsecondsSinceEpoch.toString();
|
String uniqueId() => DateTime.now().microsecondsSinceEpoch.toString();
|
||||||
|
|
||||||
|
String importedEventKey(CalendarEvent event) {
|
||||||
|
final calendarId = event.calendarId?.trim();
|
||||||
|
final externalId = event.externalId?.trim();
|
||||||
|
if (calendarId != null &&
|
||||||
|
calendarId.isNotEmpty &&
|
||||||
|
externalId != null &&
|
||||||
|
externalId.isNotEmpty) {
|
||||||
|
return '$calendarId::$externalId';
|
||||||
|
}
|
||||||
|
|
||||||
|
final fallbackCalendar = calendarId?.isNotEmpty == true
|
||||||
|
? calendarId!
|
||||||
|
: 'local';
|
||||||
|
final date = event.date.toIso8601String().split('T').first;
|
||||||
|
return '$fallbackCalendar::${event.title}::$date::${event.start}';
|
||||||
|
}
|
||||||
|
|
||||||
String normalizeJarvisUrl(String value) {
|
String normalizeJarvisUrl(String value) {
|
||||||
final trimmed = value.trim();
|
final trimmed = value.trim();
|
||||||
if (trimmed.isEmpty) return JarvisSettings.defaults().apiUrl;
|
if (trimmed.isEmpty) return JarvisSettings.defaults().apiUrl;
|
||||||
|
|||||||
Reference in New Issue
Block a user