Improve web calendar layout and shared sync state
This commit is contained in:
@@ -12,6 +12,19 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy("CalendarWeb", policy =>
|
||||||
|
{
|
||||||
|
policy
|
||||||
|
.WithOrigins(
|
||||||
|
"https://calendar.a-web.hu",
|
||||||
|
"http://localhost:5000",
|
||||||
|
"http://localhost:8080")
|
||||||
|
.AllowAnyHeader()
|
||||||
|
.AllowAnyMethod();
|
||||||
|
});
|
||||||
|
});
|
||||||
builder.Services.AddHttpClient("open-food-facts", client =>
|
builder.Services.AddHttpClient("open-food-facts", client =>
|
||||||
{
|
{
|
||||||
client.BaseAddress = new Uri("https://world.openfoodfacts.org/");
|
client.BaseAddress = new Uri("https://world.openfoodfacts.org/");
|
||||||
@@ -52,6 +65,7 @@ if (app.Environment.IsDevelopment())
|
|||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.UseCors("CalendarWeb");
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"a10d8ac38de835021c8d2f920dbf50a920ccc0
|
|||||||
|
|
||||||
_flutter.loader.load({
|
_flutter.loader.load({
|
||||||
serviceWorkerSettings: {
|
serviceWorkerSettings: {
|
||||||
serviceWorkerVersion: "2721834507" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
serviceWorkerVersion: "1030817064" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
+40248
-40153
File diff suppressed because one or more lines are too long
@@ -8,10 +8,19 @@ server {
|
|||||||
try_files $uri $uri/ /index.html;
|
try_files $uri $uri/ /index.html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location = /favicon.png {
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
try_files /favicon.png =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /manifest.json {
|
||||||
|
add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||||
|
try_files /manifest.json =404;
|
||||||
|
}
|
||||||
|
|
||||||
location = /health {
|
location = /health {
|
||||||
access_log off;
|
access_log off;
|
||||||
add_header Content-Type text/plain;
|
add_header Content-Type text/plain;
|
||||||
return 200 "ok\n";
|
return 200 "ok\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+47
-4
@@ -520,6 +520,8 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
languageCode: widget.languageCode,
|
languageCode: widget.languageCode,
|
||||||
onChanged: widget.onGoogleConnectionsChanged,
|
onChanged: widget.onGoogleConnectionsChanged,
|
||||||
onSyncedEventsChanged: widget.onSyncedEventsChanged,
|
onSyncedEventsChanged: widget.onSyncedEventsChanged,
|
||||||
|
onWorkspaceStateShouldUpload: () =>
|
||||||
|
_uploadWorkspaceState(silent: true),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -969,6 +971,27 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
.map(Person.fromJson)
|
.map(Person.fromJson)
|
||||||
.toList()
|
.toList()
|
||||||
: widget.people;
|
: widget.people;
|
||||||
|
final nextAppCalendars = state['appCalendars'] is List
|
||||||
|
? (state['appCalendars'] as List)
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.map(AppCalendar.fromJson)
|
||||||
|
.toList()
|
||||||
|
: widget.appCalendars;
|
||||||
|
final nextGoogleConnections = state['googleConnections'] is List
|
||||||
|
? (state['googleConnections'] as List)
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.map(GoogleCalendarConnection.fromJson)
|
||||||
|
.toList()
|
||||||
|
: widget.googleConnections;
|
||||||
|
final nextSyncedEvents = state['syncedEvents'] is List
|
||||||
|
? (state['syncedEvents'] as List)
|
||||||
|
.whereType<Map<String, dynamic>>()
|
||||||
|
.map(CalendarEvent.fromJson)
|
||||||
|
.toList()
|
||||||
|
: widget.syncedEvents;
|
||||||
|
final nextTodoSuggestions = state['todoSuggestions'] is List
|
||||||
|
? (state['todoSuggestions'] as List).whereType<String>().toList()
|
||||||
|
: widget.todoSuggestions;
|
||||||
final nextShopping = state['shopping'] is List
|
final nextShopping = state['shopping'] is List
|
||||||
? (state['shopping'] as List)
|
? (state['shopping'] as List)
|
||||||
.whereType<Map<String, dynamic>>()
|
.whereType<Map<String, dynamic>>()
|
||||||
@@ -991,6 +1014,10 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
..addAll(nextStock);
|
..addAll(nextStock);
|
||||||
});
|
});
|
||||||
widget.onPeopleChanged(nextPeople);
|
widget.onPeopleChanged(nextPeople);
|
||||||
|
widget.onAppCalendarsChanged(nextAppCalendars);
|
||||||
|
widget.onGoogleConnectionsChanged(nextGoogleConnections);
|
||||||
|
widget.onSyncedEventsChanged(nextSyncedEvents);
|
||||||
|
widget.onTodoSuggestionsChanged(nextTodoSuggestions);
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
_showWorkspaceMessage('K\u00f6z\u00f6s munkat\u00e9r let\u00f6ltve.');
|
_showWorkspaceMessage('K\u00f6z\u00f6s munkat\u00e9r let\u00f6ltve.');
|
||||||
}
|
}
|
||||||
@@ -1003,10 +1030,12 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _uploadWorkspaceState() async {
|
Future<void> _uploadWorkspaceState({bool silent = false}) async {
|
||||||
final workspaceToken = widget.jarvisSettings.workspaceToken?.trim();
|
final workspaceToken = widget.jarvisSettings.workspaceToken?.trim();
|
||||||
if (workspaceToken == null || workspaceToken.isEmpty) {
|
if (workspaceToken == null || workspaceToken.isEmpty) {
|
||||||
_showWorkspaceMessage('Adj meg k\u00f6z\u00f6s munkat\u00e9r tokent.');
|
if (!silent) {
|
||||||
|
_showWorkspaceMessage('Adj meg k\u00f6z\u00f6s munkat\u00e9r tokent.');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1019,6 +1048,16 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
'version': 1,
|
'version': 1,
|
||||||
'updatedAt': DateTime.now().toIso8601String(),
|
'updatedAt': DateTime.now().toIso8601String(),
|
||||||
'people': widget.people.map((person) => person.toJson()).toList(),
|
'people': widget.people.map((person) => person.toJson()).toList(),
|
||||||
|
'appCalendars': widget.appCalendars
|
||||||
|
.map((calendar) => calendar.toJson())
|
||||||
|
.toList(),
|
||||||
|
'googleConnections': widget.googleConnections
|
||||||
|
.map((connection) => connection.toJson())
|
||||||
|
.toList(),
|
||||||
|
'syncedEvents': widget.syncedEvents
|
||||||
|
.map((event) => event.toJson())
|
||||||
|
.toList(),
|
||||||
|
'todoSuggestions': widget.todoSuggestions,
|
||||||
'shopping': shopping.map((item) => item.toJson()).toList(),
|
'shopping': shopping.map((item) => item.toJson()).toList(),
|
||||||
'stock': stock.map((item) => item.toJson()).toList(),
|
'stock': stock.map((item) => item.toJson()).toList(),
|
||||||
});
|
});
|
||||||
@@ -1036,9 +1075,13 @@ class _HomeShellState extends State<HomeShell> {
|
|||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('workspace PUT ${response.statusCode}');
|
throw Exception('workspace PUT ${response.statusCode}');
|
||||||
}
|
}
|
||||||
_showWorkspaceMessage('K\u00f6z\u00f6s munkat\u00e9r felt\u00f6ltve.');
|
if (!silent) {
|
||||||
|
_showWorkspaceMessage('K\u00f6z\u00f6s munkat\u00e9r felt\u00f6ltve.');
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
_showWorkspaceMessage('Workspace hiba: $error');
|
if (!silent) {
|
||||||
|
_showWorkspaceMessage('Workspace hiba: $error');
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) setState(() => workspaceSyncing = false);
|
if (mounted) setState(() => workspaceSyncing = false);
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-69
@@ -741,34 +741,56 @@ class WeekBoard extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Row(
|
LayoutBuilder(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
builder: (context, constraints) {
|
||||||
children: List.generate(7, (index) {
|
final vertical = constraints.maxWidth < 760;
|
||||||
final date = weekStart.add(Duration(days: index));
|
final columns = List.generate(7, (index) {
|
||||||
final dayEvents =
|
final date = weekStart.add(Duration(days: index));
|
||||||
events
|
final dayEvents =
|
||||||
.where((event) => isSameDate(event.date, date))
|
events
|
||||||
.toList()
|
.where((event) => isSameDate(event.date, date))
|
||||||
..sort((a, b) => a.start.compareTo(b.start));
|
.toList()
|
||||||
final daySecondaryEvents = secondaryEvents
|
..sort((a, b) => a.start.compareTo(b.start));
|
||||||
.where((event) => isSameDate(event.date, date))
|
final daySecondaryEvents = secondaryEvents
|
||||||
.toList();
|
.where((event) => isSameDate(event.date, date))
|
||||||
return Expanded(
|
.toList();
|
||||||
child: Padding(
|
return WeekDayColumn(
|
||||||
padding: EdgeInsets.only(right: index == 6 ? 0 : 6),
|
title: days[index],
|
||||||
child: WeekDayColumn(
|
dateLabel: formatShortDate(date),
|
||||||
title: days[index],
|
active: selectedDay == index,
|
||||||
dateLabel: formatShortDate(date),
|
events: dayEvents,
|
||||||
active: selectedDay == index,
|
secondaryEvents: daySecondaryEvents,
|
||||||
events: dayEvents,
|
appCalendars: appCalendars,
|
||||||
secondaryEvents: daySecondaryEvents,
|
people: people,
|
||||||
appCalendars: appCalendars,
|
compact: vertical,
|
||||||
people: people,
|
onTap: () => onSelectDay(index),
|
||||||
onTap: () => onSelectDay(index),
|
);
|
||||||
),
|
});
|
||||||
),
|
|
||||||
|
if (vertical) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
for (var i = 0; i < columns.length; i++) ...[
|
||||||
|
columns[i],
|
||||||
|
if (i != columns.length - 1) const SizedBox(height: 8),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
for (var i = 0; i < columns.length; i++)
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(right: i == 6 ? 0 : 6),
|
||||||
|
child: columns[i],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -787,6 +809,7 @@ class WeekDayColumn extends StatelessWidget {
|
|||||||
required this.secondaryEvents,
|
required this.secondaryEvents,
|
||||||
required this.appCalendars,
|
required this.appCalendars,
|
||||||
required this.people,
|
required this.people,
|
||||||
|
this.compact = false,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -797,6 +820,7 @@ class WeekDayColumn extends StatelessWidget {
|
|||||||
final List<CalendarEvent> secondaryEvents;
|
final List<CalendarEvent> secondaryEvents;
|
||||||
final List<AppCalendar> appCalendars;
|
final List<AppCalendar> appCalendars;
|
||||||
final List<Person> people;
|
final List<Person> people;
|
||||||
|
final bool compact;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -807,7 +831,7 @@ class WeekDayColumn extends StatelessWidget {
|
|||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 180),
|
duration: const Duration(milliseconds: 180),
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
constraints: const BoxConstraints(minHeight: 190),
|
constraints: BoxConstraints(minHeight: compact ? 92 : 190),
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: active ? mint : const Color(0xFFF8F8F5),
|
color: active ? mint : const Color(0xFFF8F8F5),
|
||||||
@@ -820,46 +844,54 @@ class WeekDayColumn extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Row(
|
||||||
title,
|
children: [
|
||||||
maxLines: 1,
|
Expanded(
|
||||||
overflow: TextOverflow.ellipsis,
|
child: Text(
|
||||||
style: const TextStyle(
|
title,
|
||||||
color: ink,
|
maxLines: 1,
|
||||||
fontSize: 14,
|
overflow: TextOverflow.ellipsis,
|
||||||
fontWeight: FontWeight.w900,
|
style: const TextStyle(
|
||||||
),
|
color: ink,
|
||||||
),
|
fontSize: 14,
|
||||||
const SizedBox(height: 7),
|
fontWeight: FontWeight.w900,
|
||||||
Container(
|
),
|
||||||
width: double.infinity,
|
),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 7),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: active ? sage : Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(14),
|
|
||||||
boxShadow: active
|
|
||||||
? [
|
|
||||||
BoxShadow(
|
|
||||||
color: sage.withValues(alpha: .18),
|
|
||||||
blurRadius: 12,
|
|
||||||
offset: const Offset(0, 5),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
dateLabel,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
softWrap: false,
|
|
||||||
style: TextStyle(
|
|
||||||
color: active ? Colors.white : sage,
|
|
||||||
fontSize: 17,
|
|
||||||
letterSpacing: .5,
|
|
||||||
fontWeight: FontWeight.w900,
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: compact ? 10 : 8,
|
||||||
|
vertical: compact ? 5 : 7,
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: active ? sage : Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(14),
|
||||||
|
boxShadow: active
|
||||||
|
? [
|
||||||
|
BoxShadow(
|
||||||
|
color: sage.withValues(alpha: .18),
|
||||||
|
blurRadius: 12,
|
||||||
|
offset: const Offset(0, 5),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
dateLabel,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
softWrap: false,
|
||||||
|
style: TextStyle(
|
||||||
|
color: active ? Colors.white : sage,
|
||||||
|
fontSize: compact ? 15 : 17,
|
||||||
|
letterSpacing: .5,
|
||||||
|
fontWeight: FontWeight.w900,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
if (secondaryEvents.isNotEmpty) ...[
|
if (secondaryEvents.isNotEmpty) ...[
|
||||||
@@ -870,8 +902,8 @@ class WeekDayColumn extends StatelessWidget {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
if (events.isEmpty)
|
if (events.isEmpty)
|
||||||
const SizedBox(
|
SizedBox(
|
||||||
height: 92,
|
height: compact ? 32 : 92,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'nincs program',
|
'nincs program',
|
||||||
|
|||||||
+127
-37
@@ -1057,48 +1057,81 @@ class _BarcodeScanDialogState extends State<BarcodeScanDialog> {
|
|||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: 520,
|
width: 520,
|
||||||
height: 360,
|
height: 360,
|
||||||
child: ClipRRect(
|
child: Column(
|
||||||
borderRadius: BorderRadius.circular(18),
|
children: [
|
||||||
child: Stack(
|
if (kIsWeb)
|
||||||
fit: StackFit.expand,
|
Container(
|
||||||
children: [
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
MobileScanner(
|
padding: const EdgeInsets.all(12),
|
||||||
controller: controller,
|
decoration: BoxDecoration(
|
||||||
onDetect: (capture) {
|
color: mint,
|
||||||
if (handled) return;
|
borderRadius: BorderRadius.circular(16),
|
||||||
for (final code in capture.barcodes) {
|
),
|
||||||
final value = code.rawValue;
|
child: const Row(
|
||||||
if (value == null || value.trim().isEmpty) continue;
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
handled = true;
|
children: [
|
||||||
Navigator.pop(context, value);
|
Icon(Icons.info_outline, color: sage, size: 20),
|
||||||
break;
|
SizedBox(width: 8),
|
||||||
}
|
Expanded(
|
||||||
},
|
|
||||||
),
|
|
||||||
const Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(12),
|
|
||||||
child: DecoratedBox(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.black54,
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 8,
|
|
||||||
),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'Tartsd a vonalk\u00f3dot az el\u0151lapi kamera el\u00e9.',
|
'Weben a kamera enged\u00e9lyt a b\u00f6ng\u00e9sz\u0151 k\u00e9ri. iPhone/iPad Safari alatt: aA / Webhelybe\u00e1ll\u00edt\u00e1sok / Kamera: Enged\u00e9lyez\u00e9s, majd t\u00f6ltsd \u00fajra az oldalt.',
|
||||||
style: TextStyle(color: Colors.white),
|
style: TextStyle(color: ink, fontSize: 12.5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
Expanded(
|
||||||
),
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(18),
|
||||||
|
child: Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
MobileScanner(
|
||||||
|
controller: controller,
|
||||||
|
placeholderBuilder: (context) =>
|
||||||
|
const ScannerPermissionHint(),
|
||||||
|
errorBuilder: (context, error) =>
|
||||||
|
ScannerPermissionHint(error: error),
|
||||||
|
onDetectError: (error, stackTrace) {},
|
||||||
|
onDetect: (capture) {
|
||||||
|
if (handled) return;
|
||||||
|
for (final code in capture.barcodes) {
|
||||||
|
final value = code.rawValue;
|
||||||
|
if (value == null || value.trim().isEmpty) continue;
|
||||||
|
handled = true;
|
||||||
|
Navigator.pop(context, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black54,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'Tartsd a vonalk\u00f3dot az el\u0151lapi kamera el\u00e9.',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
@@ -1111,6 +1144,63 @@ class _BarcodeScanDialogState extends State<BarcodeScanDialog> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ScannerPermissionHint extends StatelessWidget {
|
||||||
|
const ScannerPermissionHint({super.key, this.error});
|
||||||
|
|
||||||
|
final MobileScannerException? error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
color: const Color(0xFF172B27),
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Center(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 360),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.photo_camera_outlined,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 42,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 14),
|
||||||
|
Text(
|
||||||
|
error == null
|
||||||
|
? 'Kamera enged\u00e9ly k\u00e9r\u00e9se...'
|
||||||
|
: 'A kamera nem el\u00e9rhet\u0151',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w900,
|
||||||
|
fontSize: 18,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
error == null
|
||||||
|
? 'Ha a b\u00f6ng\u00e9sz\u0151 r\u00e1k\u00e9rdez, enged\u00e9lyezd a kamer\u00e1t a vonalk\u00f3d beolvas\u00e1s\u00e1hoz.'
|
||||||
|
: 'Enged\u00e9lyezd a kamer\u00e1t a b\u00f6ng\u00e9sz\u0151 webhelybe\u00e1ll\u00edt\u00e1sain\u00e1l, majd nyisd meg \u00fajra a scannert.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(color: Colors.white70, height: 1.35),
|
||||||
|
),
|
||||||
|
if (error != null) ...[
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
error!.errorCode.name,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(color: Colors.white38, fontSize: 11),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typedef JarvisRegisterCallback =
|
typedef JarvisRegisterCallback =
|
||||||
Future<JarvisSettings> Function(JarvisSettings settings, String deviceName);
|
Future<JarvisSettings> Function(JarvisSettings settings, String deviceName);
|
||||||
typedef JarvisStatusCallback =
|
typedef JarvisStatusCallback =
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class AccountsPage extends StatefulWidget {
|
|||||||
required this.languageCode,
|
required this.languageCode,
|
||||||
required this.onChanged,
|
required this.onChanged,
|
||||||
required this.onSyncedEventsChanged,
|
required this.onSyncedEventsChanged,
|
||||||
|
this.onWorkspaceStateShouldUpload,
|
||||||
});
|
});
|
||||||
|
|
||||||
final List<GoogleCalendarConnection> connections;
|
final List<GoogleCalendarConnection> connections;
|
||||||
@@ -23,6 +24,7 @@ class AccountsPage extends StatefulWidget {
|
|||||||
final String languageCode;
|
final String languageCode;
|
||||||
final ValueChanged<List<GoogleCalendarConnection>> onChanged;
|
final ValueChanged<List<GoogleCalendarConnection>> onChanged;
|
||||||
final ValueChanged<List<CalendarEvent>> onSyncedEventsChanged;
|
final ValueChanged<List<CalendarEvent>> onSyncedEventsChanged;
|
||||||
|
final VoidCallback? onWorkspaceStateShouldUpload;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AccountsPage> createState() => _AccountsPageState();
|
State<AccountsPage> createState() => _AccountsPageState();
|
||||||
@@ -429,6 +431,7 @@ class _AccountsPageState extends State<AccountsPage>
|
|||||||
: '${synced.length} Google esem\u00e9ny szinkroniz\u00e1lva \u00e9s JSON cache-be mentve.'
|
: '${synced.length} Google esem\u00e9ny szinkroniz\u00e1lva \u00e9s JSON cache-be mentve.'
|
||||||
'${syncDetails.isEmpty ? '' : ' R\u00e9szletek: ${syncDetails.join('; ')}.'}';
|
'${syncDetails.isEmpty ? '' : ' R\u00e9szletek: ${syncDetails.join('; ')}.'}';
|
||||||
});
|
});
|
||||||
|
widget.onWorkspaceStateShouldUpload?.call();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setState(() {
|
setState(() {
|
||||||
syncing = false;
|
syncing = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user