Skip to content

Import a file as what it holds, not what it's named - #426

Merged
SunkenInTime merged 4 commits into
mainfrom
t3/import-extensionless
Nov 1, 2026
Merged

SunkenInTime merged 4 commits into
mainfrom
t3/import-extensionless

Conversation

@SunkenInTime

@SunkenInTime SunkenInTime commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Before: Import Backup on an extensionless .ica says "Imported 1 folder" and creates an empty folder. After: the strategy imports.

ci: analyze test web

Import told people it worked when their strategy never came in. A file saved with no extension is still an .ica, and an .ica is a zip, so Import Backup and drag and drop read it as a folder archive. The toast said "Imported 1 folder" and the library got an empty folder named renamed-ica.json. Any other zip did the same: a zip of photos became an empty folder called notes.txt.

#420 stops new exports losing their extension. This PR is for the files already out there. It's based on main and doesn't touch the export paths #420 changes.

What changed

Every way in now reads the file to decide what it is. _importFileKind looks inside:

  • a JSON object with mapData, alone or at the top of a zip, is a strategy
  • a zip holding a manifest or .ica files is an archive
  • anything else creates nothing and reports a failure

mapData is the one field the strategy import can't do without, so the check can't reject a strategy that used to load. A file named .ica that the check can't recognise still goes to the old strategy import and fails the way it always did.

Import .ica, Import Backup, a dropped file, the contents of a dropped folder and a file opened with Icarus all go through this now, so Import .ica takes a backup and Import Backup takes an .ica.

Three smaller changes follow from it:

  • Both open dialogs list all files. With the .ica filter, a user could not see the file they were trying to import.
  • Only .ica, .json and .zip come off a name. Bind v1.2 with its extension lost used to come in as Bind v1.
  • Import .ica and a file opened with Icarus now show the same summary toast as drag and drop, including "Imported 1 strategy."

The Import .ica dialog on main, showing one file, and on this branch, showing the extensionless files

What I checked

Windows release driver builds, main and this branch, one script against both (dropFiles is a new driver step that sends the messages the native drop target sends). The Open dialogs were the real Windows ones. The script took 148 steps with no failures and left no empty folder in the library.

File, no extension Way in main This branch
.ica Import Backup empty folder, "Imported 1 folder" strategy imports
.ica Import .ica, name typed strategy imports, file not listed strategy imports, file listed
.ica drop empty folder, "Imported 1 folder" strategy imports
v43 and v97 fixtures drop empty folder strategy imports, v97 opens with its pages
bare JSON .ica drop "No compatible strategies" strategy imports
backup drop strategy imports strategy imports
backup Import .ica "Failed to import strategy file." strategy imports
backup opened with Icarus not imported strategy imports
pre-manifest zip of .ica files drop folder with both strategies same
zip of something else Import Backup, drop empty folder, "Imported 1 folder" red failure, nothing created
random bytes drop red failure red failure
zip of something else opened with Icarus "An unexpected application error occurred." "No compatible strategies or folders were imported."

Opening a zip of something else with Icarus: an empty library and a red failure toast

Old files: I imported every distinct real .ica and backup I could find on this machine (46, from the fixtures and earlier test runs) through main and this branch. With their names intact the two builds gave identical results for all 46. With the extension gone, main made an empty folder for all 44 .ica files. This branch imported 42 and refused 2 with a failure, the same 2 that fail with their names on. Those 2 are hand-edited test files with a number where exports write a string (sortIndex).

Targeted tests pass: 92 across folder import, strategy integrity, the version guard, the import summary and library export. The new tests run all three historical fixtures with their extensions removed.

Astra reviewed it and found three things. A strategy saved as icarus-metadata looked like a backup, which was my regression, so the strategy check now runs first. Opening a backup with Icarus hid partial failures, so it now shows the summary. The picker tests ignored the filter, so they now assert it. Its third finding, that a backup inside a dropped folder is skipped, is left alone. main does that on purpose, a test pins it, and the user is told it was skipped.

What this doesn't cover

  • Web has no import. The menu items and the drop target are both off for the web beta (importFiles in platform_policy.dart). That's from reading the code, not from a browser.
  • A real Explorer drag wasn't done. The drop step replays the plugin's messages, so the Dart side is the real path, and the OS half isn't exercised.
  • macOS wasn't run. FileType.any is a plain file_picker option there, so I expect no difference.
  • An extensionless .ica inside a user-made zip is still reported as a skipped file. Exports never produce that.
  • An old folder export holding only empty folders, and no strategies, now reports a failure where main made the empty folders. Nothing was in them to lose.

RetriggerView in GreptileConfidence Score: 4/5

The PR appears safe to merge, with a non-blocking improvement to avoid reading large ZIP files twice.

Findings

  1. P2 ZIP files are read twice ▶

Summary

Imports now identify strategies and backups by their contents rather than their names.

  • Imports choose the right path based on what a file contains.
  • Imported names keep dots that are not file endings.
  • Import screens and opened files show what came in.

Checks were limited to source review; no app or tests were run.

Acknowledged exclusions from SunkenInTime: backups inside dropped folders remain intentionally skipped; extensionless strategies inside user-made ZIPs remain unsupported; legacy exports containing only empty folders now fail instead of creating folders.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Picker, drop, or file opened with Icarus] --> B[Inspect file contents]
  B --> C{File kind}
  C -->|Strategy| D[Import one strategy]
  C -->|Archive| E[Import backup or folder archive]
  C -->|Unknown| F[Report unsupported file]
  D --> G[Show import summary]
  E --> G
  F --> G
Loading

Reviews (1) · Last reviewed commit: "Check for a strategy before a manifest, ..." · Reviewed by Greptile

@coderabbitai

coderabbitai Bot commented Nov 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 25 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 50dc2513-6980-467c-807f-ec974b0d478c
Commits

Reviewing files that changed from the base of the PR and between 9e799b1 and 99c8cf5.

Files selected for processing: 6
  • lib/dev/drive/drive.dart
  • lib/main.dart
  • lib/strategy/strategy_import_export.dart
  • lib/widgets/folder_navigator.dart
  • lib/widgets/ica_drop_target.dart
  • test/strategy_folder_import_test.dart
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Comment on lines +491 to +492
final archive = ZipDecoder().decodeBytes(await file.readAsBytes());
return _zipImportKind(archive) ?? unrecognised;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 ZIP files are read twice

_importFileKind reads and decodes the whole ZIP, then _importStrategyFile or _importZipArchive immediately repeats that work. Large backups and strategies with many images now pay for an extra full-file read and archive parse. Pass the decoded archive into the selected importer to avoid repeating this work.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@SunkenInTime
SunkenInTime merged commit 07ad822 into main Nov 1, 2026
25 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant