mirror of
https://github.com/kuhyx/todo-app.git
synced 2026-11-11 13:47:45 +01:00
Some checks failed
📎 now picks any file (Android: Camera / Gallery / File; desktop: an
unfiltered dialog) and the desktop page accepts any dropped file. Photo and
screenshot formats up to 64 MB are still re-encoded and shown inline;
everything else is stored byte for byte on dufs beside the images and linked
as [name](url), shown as a row with its size that opens in the platform's
app (Android) or a new browser tab (desktop). BACKLOG.md rewrites file links
to ~/data/cloud/todo-images/ like image links.
There is no size cap, so no layer holds a file in memory: uploads and
downloads stream through DufsImageClient, writes land in a .tmp renamed when
whole (an interrupted download no longer leaves a truncated file that looks
done), the desktop page POSTs the browser's own Blob with fetch, and Android's
"File" uses file_picker, because file_selector's Android side reads the whole
file into memory on the UI thread. Verified with a 1 GB file end to end on
desktop (peak RSS +36 MB) and on an API 36 emulator.
The wrapper serves attachments from the origin that holds the GitHub token
and the notes, so only script-free types open inline (nosniff), the rest
download; writes carrying another site's Origin are refused (this also
closes cross-site overwrites of the backup files); and attachment routes run
concurrently so a long upload or a playing video cannot stall the page.
Also: cupertino_icons 2.0.0 (unused, but the freshness gate blocks the
pubspec otherwise), open_file 4.0.0 and file_picker 13.1.0 pinned.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.4 KiB
Dart
62 lines
2.4 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:todo/images/attachment_links.dart';
|
|
import 'package:todo/images/attachment_ref.dart';
|
|
|
|
void main() {
|
|
final image = AttachmentRef.tryParse('n1/a.jpg')!;
|
|
final pdf = AttachmentRef.tryParse('n1/b.pdf')!;
|
|
final other = AttachmentRef.create('n2');
|
|
|
|
test('attachmentsIn finds images and files, in order, each once', () {
|
|
final text =
|
|
'hi ${image.imageMarkdown} and\n${pdf.fileMarkdown('b.pdf')}\n'
|
|
'${other.imageMarkdown}\n${image.imageMarkdown}\n'
|
|
' [y]($kDufsBaseUrl/Keepass/a.pdf)';
|
|
final found = attachmentsIn(text);
|
|
expect([for (final a in found) a.ref], [image, pdf, other]);
|
|
expect([for (final a in found) a.isImage], [true, false, true]);
|
|
expect(found[1].name, 'b.pdf');
|
|
expect(attachmentsIn('no attachments'), isEmpty);
|
|
});
|
|
|
|
test('an image link is never also read as a file link', () {
|
|
final found = attachmentsIn(image.imageMarkdown);
|
|
expect(found, hasLength(1));
|
|
expect(found.single.isImage, isTrue);
|
|
});
|
|
|
|
test('attachmentRefsIn covers both kinds', () {
|
|
expect(
|
|
attachmentRefsIn('${image.imageMarkdown}\n${pdf.fileMarkdown('b')}'),
|
|
{image, pdf},
|
|
);
|
|
});
|
|
|
|
test('attachmentOfLine only matches a line that is just one link', () {
|
|
expect(attachmentOfLine(' ${image.imageMarkdown} ')!.ref, image);
|
|
expect(attachmentOfLine('')!.name, 'alt text');
|
|
final file = attachmentOfLine(pdf.fileMarkdown('Raport.pdf'))!;
|
|
expect(file.ref, pdf);
|
|
expect(file.isImage, isFalse);
|
|
expect(file.name, 'Raport.pdf');
|
|
expect(attachmentOfLine('see ${image.imageMarkdown}'), isNull);
|
|
expect(attachmentOfLine('plain'), isNull);
|
|
});
|
|
|
|
test('backlog paths round-trip exactly, images and files alike', () {
|
|
final text =
|
|
'a\n${image.imageMarkdown}\n${pdf.fileMarkdown('b.pdf')}\n'
|
|
'';
|
|
final disk = toBacklogAttachmentPaths(text);
|
|
expect(disk, contains(''));
|
|
expect(disk, contains('[b.pdf]($kBacklogImagesRoot/n1/b.pdf)'));
|
|
expect(disk, isNot(contains('$kDufsBaseUrl/$kImagesDir')));
|
|
expect(fromBacklogAttachmentPaths(disk), text);
|
|
});
|
|
|
|
test('a bare path outside a link is left alone', () {
|
|
const text = 'see $kBacklogImagesRoot/n1/b.pdf';
|
|
expect(fromBacklogAttachmentPaths(text), text);
|
|
});
|
|
}
|