Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions flutter_app/lib/dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class _Dialog extends StatefulWidget {

class _DialogState extends State<_Dialog> {
late final _name = TextEditingController(text: widget.taskToEdit?.title);
late final _attachmentNote = TextEditingController();
late var _done = widget.taskToEdit?.done ?? false;

@override
Expand All @@ -30,6 +31,7 @@ class _DialogState extends State<_Dialog> {
mainAxisSize: MainAxisSize.min,
children: [
_textInput(_name, "Name"),
_textInput(_attachmentNote, "Attachment Note (optional)"),
_doneSwitch,
],
),
Expand All @@ -45,6 +47,10 @@ class _DialogState extends State<_Dialog> {
title: _name.text,
done: _done,
deleted: false,
// Pass the attachment note text if provided
attachment: _attachmentNote.text.isNotEmpty
? {"note": _attachmentNote.text}
: null,
);
Navigator.of(context).pop(task);
},
Expand Down
74 changes: 73 additions & 1 deletion flutter_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,24 @@ class _DittoExampleState extends State<DittoExample> {
final task = await showAddTaskDialog(context);
if (task == null) return;

Map<String, dynamic> taskJson = task.toJson();

// If the task has an attachment note, create a Ditto attachment
if (task.attachment != null && task.attachment!['note'] != null) {
final noteText = task.attachment!['note'] as String;
final noteBytes = Uint8List.fromList(noteText.codeUnits);

// Create attachment from the note bytes
final attachment = await _ditto!.store.newAttachment(noteBytes);

// Replace the placeholder with the actual attachment token
taskJson['attachment'] = attachment;
}

// https://docs.ditto.live/sdk/latest/crud/create
await _ditto!.store.execute(
"INSERT INTO tasks DOCUMENTS (:task)",
arguments: {"task": task.toJson()},
arguments: {"task": taskJson},
);
}

Expand All @@ -112,6 +126,50 @@ class _DittoExampleState extends State<DittoExample> {
await _ditto!.store.execute("EVICT FROM tasks WHERE true");
}

Future<void> _showAttachment(Map<String, dynamic> attachmentToken) async {
// Show loading dialog
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const Center(child: CircularProgressIndicator()),
);

String? attachmentContent;

// Fetch the attachment using the Ditto attachment API
_ditto!.store.fetchAttachment(
attachmentToken,
(event) async {
if (event is AttachmentFetchEventCompleted) {
// Get the attachment data
final bytes = await event.attachment.data;
// Convert bytes back to string
attachmentContent = String.fromCharCodes(bytes);

// Close loading dialog
if (mounted) Navigator.of(context).pop();

// Show the attachment content in a dialog
if (mounted) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("Attachment Content"),
content: Text(attachmentContent ?? "No content"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text("Close"),
),
],
),
);
}
}
},
);
}

@override
Widget build(BuildContext context) {
if (_ditto == null) return _loading;
Expand Down Expand Up @@ -207,6 +265,20 @@ class _DittoExampleState extends State<DittoExample> {
secondaryBackground: _dismissibleBackground(false),
child: CheckboxListTile(
title: Text(task.title),
subtitle: task.attachment != null
? Row(
children: [
const Icon(Icons.attachment, size: 16),
const SizedBox(width: 4),
Expanded(
child: TextButton(
onPressed: () => _showAttachment(task.attachment!),
child: const Text("View Attachment"),
),
),
],
)
: null,
value: task.done,
onChanged: (value) => _ditto!.store.execute(
"UPDATE tasks SET done = $value WHERE _id = '${task.id}'",
Expand Down
3 changes: 3 additions & 0 deletions flutter_app/lib/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ class Task {
final String title;
final bool done;
final bool deleted;
@JsonKey(includeIfNull: false)
final Map<String, dynamic>? attachment;

const Task({
this.id,
required this.title,
required this.done,
required this.deleted,
this.attachment,
});

factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
Expand Down
2 changes: 2 additions & 0 deletions flutter_app/lib/task.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions flutter_app/macos/Runner/DebugProfile.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
8 changes: 8 additions & 0 deletions flutter_app/macos/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,13 @@
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Uses Bluetooth to connect and sync with nearby devices.</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Uses WiFi to connect and sync with nearby devices.</string>
<key>NSBonjourServices</key>
<array>
<string>_http-alt._tcp.</string>
</array>
</dict>
</plist>
4 changes: 4 additions & 0 deletions flutter_app/macos/Runner/Release.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
Loading