max / goingson
- Co-Authored-By
- Claude Opus 4.8 <noreply@anthropic.com>
29 files changed,
+794 insertions,
-5708 deletions
| @@ -18,20 +18,6 @@ | |||
| 18 | 18 | "generic-array", | |
| 19 | 19 | ] | |
| 20 | 20 | ||
| 21 | - | [[package]] | |
| 22 | - | name = "ahash" | |
| 23 | - | version = "0.8.12" | |
| 24 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 25 | - | checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" | |
| 26 | - | dependencies = [ | |
| 27 | - | "cfg-if", | |
| 28 | - | "const-random", | |
| 29 | - | "getrandom 0.3.4", | |
| 30 | - | "once_cell", | |
| 31 | - | "version_check", | |
| 32 | - | "zerocopy", | |
| 33 | - | ] | |
| 34 | - | ||
| 35 | 21 | [[package]] | |
| 36 | 22 | name = "aho-corasick" | |
| 37 | 23 | version = "1.1.4" | |
| @@ -694,26 +680,6 @@ | |||
| 694 | 680 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 695 | 681 | checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" | |
| 696 | 682 | ||
| 697 | - | [[package]] | |
| 698 | - | name = "const-random" | |
| 699 | - | version = "0.1.18" | |
| 700 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 701 | - | checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" | |
| 702 | - | dependencies = [ | |
| 703 | - | "const-random-macro", | |
| 704 | - | ] | |
| 705 | - | ||
| 706 | - | [[package]] | |
| 707 | - | name = "const-random-macro" | |
| 708 | - | version = "0.1.16" | |
| 709 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 710 | - | checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" | |
| 711 | - | dependencies = [ | |
| 712 | - | "getrandom 0.2.17", | |
| 713 | - | "once_cell", | |
| 714 | - | "tiny-keccak", | |
| 715 | - | ] | |
| 716 | - | ||
| 717 | 683 | [[package]] | |
| 718 | 684 | name = "convert_case" | |
| 719 | 685 | version = "0.4.0" | |
| @@ -837,12 +803,6 @@ | |||
| 837 | 803 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 838 | 804 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" | |
| 839 | 805 | ||
| 840 | - | [[package]] | |
| 841 | - | name = "crunchy" | |
| 842 | - | version = "0.2.4" | |
| 843 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 844 | - | checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" | |
| 845 | - | ||
| 846 | 806 | [[package]] | |
| 847 | 807 | name = "crypto-common" | |
| 848 | 808 | version = "0.1.7" | |
| @@ -1401,7 +1361,7 @@ | |||
| 1401 | 1361 | dependencies = [ | |
| 1402 | 1362 | "futures-core", | |
| 1403 | 1363 | "futures-sink", | |
| 1404 | - | "spin 0.9.8", | |
| 1364 | + | "spin", | |
| 1405 | 1365 | ] | |
| 1406 | 1366 | ||
| 1407 | 1367 | [[package]] | |
| @@ -1922,7 +1882,6 @@ | |||
| 1922 | 1882 | "futures-util", | |
| 1923 | 1883 | "goingson-core", | |
| 1924 | 1884 | "goingson-db-sqlite", | |
| 1925 | - | "goingson-plugin-runtime", | |
| 1926 | 1885 | "iana-time-zone", | |
| 1927 | 1886 | "ical", | |
| 1928 | 1887 | "icalendar", | |
| @@ -1962,26 +1921,6 @@ | |||
| 1962 | 1921 | "uuid", | |
| 1963 | 1922 | ] | |
| 1964 | 1923 | ||
| 1965 | - | [[package]] | |
| 1966 | - | name = "goingson-plugin-runtime" | |
| 1967 | - | version = "0.4.0" | |
| 1968 | - | dependencies = [ | |
| 1969 | - | "async-trait", | |
| 1970 | - | "chrono", | |
| 1971 | - | "csv", | |
| 1972 | - | "goingson-core", | |
| 1973 | - | "notify", | |
| 1974 | - | "rhai", | |
| 1975 | - | "serde", | |
| 1976 | - | "serde_json", | |
| 1977 | - | "tempfile", | |
| 1978 | - | "thiserror 2.0.18", | |
| 1979 | - | "tokio", | |
| 1980 | - | "toml 0.8.2", | |
| 1981 | - | "tracing", | |
| 1982 | - | "uuid", | |
| 1983 | - | ] | |
| 1984 | - | ||
| 1985 | 1924 | [[package]] | |
| 1986 | 1925 | name = "gtk" | |
| 1987 | 1926 | version = "0.18.2" | |
| @@ -2771,7 +2710,7 @@ | |||
| 2771 | 2710 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2772 | 2711 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" | |
| 2773 | 2712 | dependencies = [ | |
| 2774 | - | "spin 0.9.8", | |
| 2713 | + | "spin", | |
| 2775 | 2714 | ] | |
| 2776 | 2715 | ||
| 2777 | 2716 | [[package]] | |
| @@ -3191,15 +3130,6 @@ | |||
| 3191 | 3130 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3192 | 3131 | checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" | |
| 3193 | 3132 | ||
| 3194 | - | [[package]] | |
| 3195 | - | name = "no-std-compat" | |
| 3196 | - | version = "0.4.1" | |
| 3197 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3198 | - | checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" | |
| 3199 | - | dependencies = [ | |
| 3200 | - | "spin 0.5.2", | |
| 3201 | - | ] | |
| 3202 | - | ||
| 3203 | 3133 | [[package]] | |
| 3204 | 3134 | name = "nodrop" | |
| 3205 | 3135 | version = "0.1.14" | |
| @@ -3502,9 +3432,6 @@ | |||
| 3502 | 3432 | version = "1.21.4" | |
| 3503 | 3433 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3504 | 3434 | checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" | |
| 3505 | - | dependencies = [ | |
| 3506 | - | "portable-atomic", | |
| 3507 | - | ] | |
| 3508 | 3435 | ||
| 3509 | 3436 | [[package]] | |
| 3510 | 3437 | name = "opaque-debug" | |
| @@ -4049,12 +3976,6 @@ | |||
| 4049 | 3976 | "universal-hash", | |
| 4050 | 3977 | ] | |
| 4051 | 3978 | ||
| 4052 | - | [[package]] | |
| 4053 | - | name = "portable-atomic" | |
| 4054 | - | version = "1.13.1" | |
| 4055 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4056 | - | checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" | |
| 4057 | - | ||
| 4058 | 3979 | [[package]] | |
| 4059 | 3980 | name = "potential_utf" | |
| 4060 | 3981 | version = "0.1.4" | |
| @@ -4539,36 +4460,6 @@ | |||
| 4539 | 4460 | "windows-sys 0.60.2", | |
| 4540 | 4461 | ] | |
| 4541 | 4462 | ||
| 4542 | - | [[package]] | |
| 4543 | - | name = "rhai" | |
| 4544 | - | version = "1.24.0" | |
| 4545 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4546 | - | checksum = "1f9ef5dabe4c0b43d8f1187dc6beb67b53fe607fff7e30c5eb7f71b814b8c2c1" | |
| 4547 | - | dependencies = [ | |
| 4548 | - | "ahash", | |
| 4549 | - | "bitflags 2.11.0", | |
| 4550 | - | "no-std-compat", | |
| 4551 | - | "num-traits", | |
| 4552 | - | "once_cell", | |
| 4553 | - | "rhai_codegen", | |
| 4554 | - | "serde", | |
| 4555 | - | "smallvec", | |
| 4556 | - | "smartstring", | |
| 4557 | - | "thin-vec", | |
| 4558 | - | "web-time", | |
| 4559 | - | ] | |
| 4560 | - | ||
| 4561 | - | [[package]] | |
| 4562 | - | name = "rhai_codegen" | |
| 4563 | - | version = "3.1.0" | |
| 4564 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 4565 | - | checksum = "d4322a2a4e8cf30771dd9f27f7f37ca9ac8fe812dddd811096a98483080dabe6" | |
| 4566 | - | dependencies = [ | |
| 4567 | - | "proc-macro2", | |
| 4568 | - | "quote", | |
| 4569 | - | "syn 2.0.117", | |
| 4570 | - | ] | |
| 4571 | - | ||
| 4572 | 4463 | [[package]] | |
| 4573 | 4464 | name = "ring" | |
| 4574 | 4465 | version = "0.17.14" | |
| @@ -5202,18 +5093,6 @@ | |||
| 5202 | 5093 | "serde", | |
| 5203 | 5094 | ] | |
| 5204 | 5095 | ||
| 5205 | - | [[package]] | |
| 5206 | - | name = "smartstring" | |
| 5207 | - | version = "1.0.1" | |
| 5208 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 5209 | - | checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" | |
| 5210 | - | dependencies = [ | |
| 5211 | - | "autocfg", | |
| 5212 | - | "serde", | |
| 5213 | - | "static_assertions", | |
| 5214 | - | "version_check", | |
| 5215 | - | ] | |
| 5216 | - | ||
| 5217 | 5096 | [[package]] | |
| 5218 | 5097 | name = "socket2" | |
| 5219 | 5098 | version = "0.6.3" | |
| @@ -5272,12 +5151,6 @@ | |||
| 5272 | 5151 | "system-deps", | |
| 5273 | 5152 | ] | |
| 5274 | 5153 | ||
| 5275 | - | [[package]] | |
| 5276 | - | name = "spin" | |
| 5277 | - | version = "0.5.2" | |
| 5278 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 5279 | - | checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | |
| 5280 | - | ||
| 5281 | 5154 | [[package]] | |
| 5282 | 5155 | name = "spin" | |
| 5283 | 5156 | version = "0.9.8" | |
| @@ -5499,12 +5372,6 @@ | |||
| 5499 | 5372 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 5500 | 5373 | checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" | |
| 5501 | 5374 | ||
| 5502 | - | [[package]] | |
| 5503 | - | name = "static_assertions" | |
| 5504 | - | version = "1.1.0" | |
| 5505 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 5506 | - | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | |
| 5507 | - | ||
| 5508 | 5375 | [[package]] | |
| 5509 | 5376 | name = "stop-token" | |
| 5510 | 5377 | version = "0.7.0" | |
| @@ -6215,15 +6082,6 @@ | |||
| 6215 | 6082 | "toml 0.8.2", | |
| 6216 | 6083 | ] | |
| 6217 | 6084 | ||
| 6218 | - | [[package]] | |
| 6219 | - | name = "thin-vec" | |
| 6220 | - | version = "0.2.14" | |
| 6221 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 6222 | - | checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" | |
| 6223 | - | dependencies = [ | |
| 6224 | - | "serde", | |
| 6225 | - | ] | |
| 6226 | - | ||
| 6227 | 6085 | [[package]] | |
| 6228 | 6086 | name = "thiserror" | |
| 6229 | 6087 | version = "1.0.69" | |
| @@ -6304,15 +6162,6 @@ | |||
| 6304 | 6162 | "time-core", | |
| 6305 | 6163 | ] | |
| 6306 | 6164 | ||
| 6307 | - | [[package]] | |
| 6308 | - | name = "tiny-keccak" | |
| 6309 | - | version = "2.0.2" | |
| 6310 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 6311 | - | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" | |
| 6312 | - | dependencies = [ | |
| 6313 | - | "crunchy", | |
| 6314 | - | ] | |
| 6315 | - | ||
| 6316 | 6165 | [[package]] | |
| 6317 | 6166 | name = "tinystr" | |
| 6318 | 6167 | version = "0.8.2" | |
| @@ -7046,16 +6895,6 @@ | |||
| 7046 | 6895 | "wasm-bindgen", | |
| 7047 | 6896 | ] | |
| 7048 | 6897 | ||
| 7049 | - | [[package]] | |
| 7050 | - | name = "web-time" | |
| 7051 | - | version = "1.1.0" | |
| 7052 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 7053 | - | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" | |
| 7054 | - | dependencies = [ | |
| 7055 | - | "js-sys", | |
| 7056 | - | "wasm-bindgen", | |
| 7057 | - | ] | |
| 7058 | - | ||
| 7059 | 6898 | [[package]] | |
| 7060 | 6899 | name = "web_atoms" | |
| 7061 | 6900 | version = "0.1.3" |
| @@ -2,7 +2,6 @@ | |||
| 2 | 2 | members = [ | |
| 3 | 3 | "crates/core", | |
| 4 | 4 | "crates/db-sqlite", | |
| 5 | - | "crates/plugin-runtime", | |
| 6 | 5 | "src-tauri", | |
| 7 | 6 | ] | |
| 8 | 7 | default-members = ["src-tauri"] | |
| @@ -61,8 +60,7 @@ | |||
| 61 | 60 | tauri-plugin-updater = "2" | |
| 62 | 61 | tauri-plugin-process = "2" | |
| 63 | 62 | ||
| 64 | - | # Plugin system | |
| 65 | - | rhai = { version = "1.17", features = ["sync", "serde"] } | |
| 63 | + | # Filesystem watching (db change notifications) | |
| 66 | 64 | notify = "6.0" | |
| 67 | 65 | notify-debouncer-mini = "0.4" | |
| 68 | 66 | theme-common = { path = "../../MNW/shared/theme-common" } | |
| @@ -98,4 +96,3 @@ | |||
| 98 | 96 | # Internal crates | |
| 99 | 97 | goingson-core = { path = "crates/core" } | |
| 100 | 98 | goingson-db-sqlite = { path = "crates/db-sqlite" } | |
| 101 | - | goingson-plugin-runtime = { path = "crates/plugin-runtime" } |
| @@ -50,10 +50,9 @@ | |||
| 50 | 50 | |-------|------|------| | |
| 51 | 51 | | `goingson-core` | `crates/core/` | Domain models, repository traits, error types, business logic. No database dependency (optional sqlx feature for type derives). | | |
| 52 | 52 | | `goingson-db-sqlite` | `crates/db-sqlite/` | SQLite persistence via sqlx. Repository implementations, FTS5 full-text search, migrations. | | |
| 53 | - | | `goingson-plugin-runtime` | `crates/plugin-runtime/` | Rhai scripting engine for import plugins (CSV, custom formats). File watching for hot-reload. | | |
| 54 | - | | `goingson-desktop` | `src-tauri/` | Tauri 2 desktop shell. Commands (thin wrappers over library crates), frontend (vanilla HTML/CSS/JS), OAuth flows, email sync, SyncKit integration. | | |
| 53 | + | | `goingson-desktop` | `src-tauri/` | Tauri 2 desktop shell. Commands (thin wrappers over library crates), native CSV/vCard/iCal import, frontend (vanilla HTML/CSS/JS), OAuth flows, email sync, SyncKit integration. | | |
| 55 | 54 | ||
| 56 | - | Dependency flow: `core` is leaf -> `db-sqlite` and `plugin-runtime` depend on `core` -> `src-tauri` depends on all three plus `synckit-client`. | |
| 55 | + | Dependency flow: `core` is leaf -> `db-sqlite` depends on `core` -> `src-tauri` depends on both plus `synckit-client`. | |
| 57 | 56 | ||
| 58 | 57 | ## Features | |
| 59 | 58 | ||
| @@ -64,7 +63,7 @@ | |||
| 64 | 63 | - **Projects** -- per-project dashboards (tasks + events + emails), milestones | |
| 65 | 64 | - **Search** -- FTS5 full-text search across all entity types | |
| 66 | 65 | - **Cloud sync** -- SyncKit integration with E2E encryption | |
| 67 | - | - **Plugins** -- Rhai scripting for CSV/data import | |
| 66 | + | - **Import** -- native CSV/TSV import (entity type auto-detected), plus vCard and iCalendar | |
| 68 | 67 | - **Themes** -- built-in light and dark themes with system auto-detection (see `src-tauri/frontend/themes/helix/`) | |
| 69 | 68 | - **Keyboard shortcuts** -- vim-style navigation throughout | |
| 70 | 69 | - **Platforms** -- macOS (primary), Windows, Linux; iOS in development |
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | Email, calendar, tasks in one place. Project management for individuals and small teams. | |
| 4 | 4 | ||
| 5 | - | A Rust-based productivity application built with Tauri 2 (Rust backend + Vanilla JS frontend), SQLite with sqlx 0.8, and a "Neobrute" design aesthetic. 3 crates: `core` (domain models), `db-sqlite` (repository), `plugin-runtime` (Rhai plugins). | |
| 5 | + | A Rust-based productivity application built with Tauri 2 (Rust backend + Vanilla JS frontend), SQLite with sqlx 0.8, and a "Neobrute" design aesthetic. 2 crates: `core` (domain models), `db-sqlite` (repository). | |
| 6 | 6 | ||
| 7 | 7 | ## High-Level Overview | |
| 8 | 8 | ||
| @@ -31,9 +31,6 @@ | |||
| 31 | 31 | │ sqlite │ | |
| 32 | 32 | │ (Desktop) │ | |
| 33 | 33 | └──────────────────┘ | |
| 34 | - | ||
| 35 | - | Additional crates: | |
| 36 | - | plugin-runtime Rhai plugin system (import plugins) | |
| 37 | 34 | ``` | |
| 38 | 35 | ||
| 39 | 36 | ## Workspace Structure | |
| @@ -42,8 +39,7 @@ | |||
| 42 | 39 | goingson/ | |
| 43 | 40 | ├── crates/ | |
| 44 | 41 | │ ├── core/ # Domain models, traits, business logic | |
| 45 | - | │ ├── db-sqlite/ # SQLite repository implementations | |
| 46 | - | │ └── plugin-runtime/ # Rhai plugin system | |
| 42 | + | │ └── db-sqlite/ # SQLite repository implementations | |
| 47 | 43 | ├── src-tauri/ # Tauri desktop app (single-user) | |
| 48 | 44 | └── migrations/ | |
| 49 | 45 | └── sqlite/ # SQLite schema migrations (33 files) | |
| @@ -53,10 +49,8 @@ | |||
| 53 | 49 | ||
| 54 | 50 | ``` | |
| 55 | 51 | goingson-desktop (src-tauri) | |
| 56 | - | ├── goingson-db-sqlite | |
| 57 | - | │ └── goingson-core | |
| 58 | - | └── plugin-runtime | |
| 59 | - | └── goingson-core (for shared types) | |
| 52 | + | └── goingson-db-sqlite | |
| 53 | + | └── goingson-core | |
| 60 | 54 | ``` | |
| 61 | 55 | ||
| 62 | 56 | ## Core Crate (`crates/core/`) | |
| @@ -165,7 +159,7 @@ | |||
| 165 | 159 | ├── weekly_review.rs # Weekly review workflow | |
| 166 | 160 | ├── saved_views.rs # Custom filter views | |
| 167 | 161 | ├── milestone.rs # Milestone CRUD and reordering | |
| 168 | - | ├── plugin.rs # Plugin registry, hot-reload, import | |
| 162 | + | ├── import.rs # Native CSV/TSV import (preview + execute) | |
| 169 | 163 | ├── oauth.rs # OAuth2 flows (Fastmail, Google, Microsoft) | |
| 170 | 164 | ├── export.rs # JSON, CSV, ICS export; backup/restore | |
| 171 | 165 | ├── sync.rs # Cloud sync via SyncKit |
| @@ -18,7 +18,6 @@ | |||
| 18 | 18 | [dependencies] | |
| 19 | 19 | goingson-core = { workspace = true } | |
| 20 | 20 | goingson-db-sqlite = { workspace = true } | |
| 21 | - | goingson-plugin-runtime = { workspace = true } | |
| 22 | 21 | synckit-client = { path = "../../../MNW/shared/synckit-client" } | |
| 23 | 22 | ||
| 24 | 23 | # Tauri |
| @@ -567,7 +567,6 @@ | |||
| 567 | 567 | <button class="settings-nav-item" data-section="notifications" onclick="GoingsOn.settings.showSection('notifications')">Notifications</button> | |
| 568 | 568 | <button class="settings-nav-item" data-section="email" onclick="GoingsOn.settings.showSection('email')">Email Accounts</button> | |
| 569 | 569 | <button class="settings-nav-item" data-section="planning" onclick="GoingsOn.settings.showSection('planning')">Planning & Review</button> | |
| 570 | - | <button class="settings-nav-item" data-section="plugins" onclick="GoingsOn.settings.showSection('plugins')">Plugins</button> | |
| 571 | 570 | <button class="settings-nav-item" data-section="sync" onclick="GoingsOn.settings.showSection('sync')">Cloud Sync</button> | |
| 572 | 571 | <button class="settings-nav-item" data-section="data" onclick="GoingsOn.settings.showSection('data')">Import & Export</button> | |
| 573 | 572 | <button class="settings-nav-item" data-section="about" onclick="GoingsOn.settings.showSection('about')">About</button> |
| @@ -263,15 +263,9 @@ | |||
| 263 | 263 | $crate::commands::get_custom_themes_dir, | |
| 264 | 264 | $crate::commands::import_theme, | |
| 265 | 265 | $crate::commands::export_theme, | |
| 266 | - | // Plugins | |
| 267 | - | $crate::commands::list_import_plugins, | |
| 268 | - | $crate::commands::list_enabled_import_plugins, | |
| 269 | - | $crate::commands::get_plugins_for_extension, | |
| 266 | + | // Import (CSV/TSV) | |
| 270 | 267 | $crate::commands::preview_import, | |
| 271 | 268 | $crate::commands::execute_import, | |
| 272 | - | $crate::commands::enable_plugin, | |
| 273 | - | $crate::commands::disable_plugin, | |
| 274 | - | $crate::commands::reload_plugin, | |
| 275 | 269 | // Import (VCF/ICS) | |
| 276 | 270 | $crate::commands::preview_vcf, | |
| 277 | 271 | $crate::commands::import_vcf, | |
| @@ -295,8 +289,6 @@ | |||
| 295 | 289 | /// Build the Tauri app with all commands registered. | |
| 296 | 290 | /// Shared between desktop `main()` and mobile entry point. | |
| 297 | 291 | pub fn build_mobile_app() -> tauri::Builder<tauri::Wry> { | |
| 298 | - | use commands::PluginState; | |
| 299 | - | use goingson_plugin_runtime::PluginRegistry; | |
| 300 | 292 | use state::AppState; | |
| 301 | 293 | use std::sync::Arc; | |
| 302 | 294 | use tauri::Manager; | |
| @@ -327,17 +319,6 @@ | |||
| 327 | 319 | app_handle.manage(Arc::new(state)); | |
| 328 | 320 | }); | |
| 329 | 321 | ||
| 330 | - | // Initialize plugin system | |
| 331 | - | let config_dir = app.path().app_config_dir() | |
| 332 | - | .expect("Failed to get config dir"); | |
| 333 | - | let plugins_dir = config_dir.join("plugins"); | |
| 334 | - | let mut plugin_registry = PluginRegistry::new(&plugins_dir) | |
| 335 | - | .expect("Failed to initialize plugin registry"); | |
| 336 | - | if let Err(e) = plugin_registry.initialize() { | |
| 337 | - | tracing::warn!("Failed to load some plugins: {}", e); | |
| 338 | - | } | |
| 339 | - | app.manage(PluginState::new(plugin_registry)); | |
| 340 | - | ||
| 341 | 322 | // Background services that work on all platforms | |
| 342 | 323 | let cancel_token = CancellationToken::new(); | |
| 343 | 324 | app.manage(cancel_token.clone()); |
| @@ -7,9 +7,7 @@ | |||
| 7 | 7 | use goingson_desktop::commands; | |
| 8 | 8 | use goingson_desktop::email_sync_scheduler; | |
| 9 | 9 | use goingson_desktop::sync_scheduler; | |
| 10 | - | use goingson_desktop::commands::PluginState; | |
| 11 | 10 | use goingson_desktop::state::AppState; | |
| 12 | - | use goingson_plugin_runtime::PluginRegistry; | |
| 13 | 11 | use std::sync::Arc; | |
| 14 | 12 | use std::sync::atomic::{AtomicBool, Ordering}; | |
| 15 | 13 | use tauri::menu::{Menu, MenuItem, PredefinedMenuItem, Submenu}; | |
| @@ -312,20 +310,6 @@ | |||
| 312 | 310 | app_handle.manage(Arc::new(state)); | |
| 313 | 311 | }); | |
| 314 | 312 | ||
| 315 | - | // Initialize plugin system | |
| 316 | - | let config_dir = app.path().app_config_dir() | |
| 317 | - | .expect("Failed to get config dir"); | |
| 318 | - | let plugins_dir = config_dir.join("plugins"); | |
| 319 | - | let mut plugin_registry = PluginRegistry::new(&plugins_dir) | |
| 320 | - | .expect("Failed to initialize plugin registry"); | |
| 321 | - | ||
| 322 | - | // Load enabled plugins | |
| 323 | - | if let Err(e) = plugin_registry.initialize() { | |
| 324 | - | tracing::warn!("Failed to load some plugins: {}", e); | |
| 325 | - | } | |
| 326 | - | ||
| 327 | - | app.manage(PluginState::new(plugin_registry)); | |
| 328 | - | ||
| 329 | 313 | // Create shutdown coordination handles | |
| 330 | 314 | let cancel_token = CancellationToken::new(); | |
| 331 | 315 | let db_watcher_shutdown = Arc::new(AtomicBool::new(false)); |
| @@ -42,7 +42,7 @@ | |||
| 42 | 42 | pub mod id_types; | |
| 43 | 43 | pub mod models; | |
| 44 | 44 | pub mod parser; | |
| 45 | - | pub mod plugin; | |
| 45 | + | pub mod import; | |
| 46 | 46 | pub mod recurrence; | |
| 47 | 47 | pub mod repository; | |
| 48 | 48 | pub mod search_parser; | |
| @@ -83,10 +83,9 @@ | |||
| 83 | 83 | pub use recurrence::{calculate_next_due, calculate_next_due_in_tz, calculate_next_due_with_day, calculate_next_due_with_day_in_tz, calculate_next_due_rich, calculate_next_due_rich_in_tz, expand_recurrence, expand_recurrence_in_tz, should_recur}; | |
| 84 | 84 | pub use repository::*; | |
| 85 | 85 | pub use urgency::calculate_urgency; | |
| 86 | - | pub use plugin::{ | |
| 87 | - | ExportPluginConfig, ImportEntityType, ImportExecuteResult, ImportFailure, ImportItem, | |
| 88 | - | ImportItemData, ImportOptions, ImportParseResult, ImportPluginConfig, ImportProgress, | |
| 89 | - | ImportProjectData, ImportTaskData, ImportEventData, PluginCapabilities, PluginMeta, PluginType, | |
| 86 | + | pub use import::{ | |
| 87 | + | ImportEntityType, ImportEventData, ImportExecuteResult, ImportFailure, ImportItem, | |
| 88 | + | ImportItemData, ImportOptions, ImportParseResult, ImportProjectData, ImportTaskData, | |
| 90 | 89 | }; | |
| 91 | 90 | pub use email_id::deterministic_email_id; | |
| 92 | 91 | pub use validation::Validate; |
| @@ -328,16 +328,11 @@ | |||
| 328 | 328 | setUpdateCheckOnLaunch: (enabled) => invoke('set_update_check_on_launch', { enabled }), | |
| 329 | 329 | }, | |
| 330 | 330 | ||
| 331 | - | // Plugins — Rhai-based import plugin management | |
| 332 | - | plugins: { | |
| 333 | - | listImport: () => invoke('list_import_plugins'), // All available (enabled + disabled) | |
| 334 | - | listEnabled: () => invoke('list_enabled_import_plugins'), | |
| 335 | - | getForExtension: (extension) => invoke('get_plugins_for_extension', { extension }), // Match file → compatible plugins | |
| 336 | - | preview: (pluginId, filePath, options = {}) => invoke('preview_import', { pluginId, filePath, options }), // Dry-run parse | |
| 337 | - | execute: (pluginId, filePath, options = {}) => invoke('execute_import', { pluginId, filePath, options }), // Create entities in DB | |
| 338 | - | enable: (pluginId) => invoke('enable_plugin', { pluginId }), // Symlink available/ → enabled/ | |
| 339 | - | disable: (pluginId) => invoke('disable_plugin', { pluginId }), | |
| 340 | - | reload: (pluginId) => invoke('reload_plugin', { pluginId }), // Re-read + recompile from disk | |
| 331 | + | // Import — native CSV/TSV import (entity type auto-detected from headers) | |
| 332 | + | import: { | |
| 333 | + | preview: (filePath, options = {}) => invoke('preview_import', { input: { filePath, options } }), // Dry-run parse | |
| 334 | + | execute: (filePath, options = {}, selectedIndices = []) => | |
| 335 | + | invoke('execute_import', { input: { filePath, options, selectedIndices } }), // Create entities in DB | |
| 341 | 336 | }, | |
| 342 | 337 | ||
| 343 | 338 | // Attachments — file attachments on tasks and projects |
| @@ -1,55 +1,36 @@ | |||
| 1 | 1 | /** | |
| 2 | 2 | * GoingsOn - Import Module | |
| 3 | - | * Handles importing data from files using plugins. | |
| 3 | + | * | |
| 4 | + | * Imports tasks, projects, or events from a CSV/TSV file. The entity type is | |
| 5 | + | * auto-detected from the header columns by the native importer (Rust). CSV is | |
| 6 | + | * GoingsOn's import interchange format; convert other tools' exports to CSV | |
| 7 | + | * with a separate converter, then bring them in here. | |
| 4 | 8 | */ | |
| 5 | 9 | ||
| 6 | 10 | (function() { | |
| 7 | 11 | 'use strict'; | |
| 8 | 12 | const esc = GoingsOn.utils.escapeHtml; | |
| 9 | - | const escAttr = GoingsOn.utils.escapeAttrValue; | |
| 10 | 13 | const escAttrVal = GoingsOn.utils.escapeAttrValue; | |
| 11 | 14 | ||
| 12 | 15 | // ============ State ============ | |
| 13 | 16 | ||
| 14 | - | let availablePlugins = []; | |
| 15 | - | let selectedPlugin = null; | |
| 16 | 17 | let selectedFilePath = null; | |
| 17 | 18 | let previewData = null; | |
| 18 | 19 | ||
| 19 | 20 | // ============ Import Wizard ============ | |
| 20 | 21 | ||
| 21 | 22 | /** | |
| 22 | - | * Opens the import wizard modal. | |
| 23 | + | * Opens the import modal (choose file, then preview and confirm). | |
| 23 | 24 | */ | |
| 24 | - | async function openImportModal() { | |
| 25 | - | // Reset state | |
| 26 | - | selectedPlugin = null; | |
| 25 | + | function openImportModal() { | |
| 27 | 26 | selectedFilePath = null; | |
| 28 | 27 | previewData = null; | |
| 29 | 28 | ||
| 30 | - | // Load available plugins | |
| 31 | - | try { | |
| 32 | - | availablePlugins = await GoingsOn.api.plugins.listEnabled(); | |
| 33 | - | } catch (err) { | |
| 34 | - | GoingsOn.ui.showToast('Failed to load import plugins: ' + GoingsOn.utils.getErrorMessage(err), 'error'); | |
| 35 | - | return; | |
| 36 | - | } | |
| 37 | - | ||
| 38 | - | if (availablePlugins.length === 0) { | |
| 39 | - | GoingsOn.ui.showToast('No import plugins available. Enable plugins in Settings.', 'info'); | |
| 40 | - | return; | |
| 41 | - | } | |
| 42 | - | ||
| 43 | 29 | const content = ` | |
| 44 | 30 | <div class="import-wizard"> | |
| 45 | - | <div class="import-step" id="import-step-1"> | |
| 46 | - | <h3>1. Select Import Source</h3> | |
| 47 | - | <div class="plugin-selector"> | |
| 48 | - | ${availablePlugins.map(p => renderPluginOption(p)).join('')} | |
| 49 | - | </div> | |
| 50 | - | </div> | |
| 51 | - | <div class="import-step hidden" id="import-step-2"> | |
| 52 | - | <h3>2. Select File</h3> | |
| 31 | + | <div class="import-step" id="import-step-file"> | |
| 32 | + | <h3>1. Choose a CSV or TSV file</h3> | |
| 33 | + | <p class="import-hint">Columns are matched by name (description, due, priority, project, tags for tasks; start/end for events; name/type for projects).</p> | |
| 53 | 34 | <div class="file-selector"> | |
| 54 | 35 | <button class="btn btn-primary" id="select-file-btn" onclick="GoingsOn.import.selectFile()"> | |
| 55 | 36 | Choose File... | |
| @@ -57,8 +38,8 @@ | |||
| 57 | 38 | <span id="selected-file-name" class="selected-file-name"></span> | |
| 58 | 39 | </div> | |
| 59 | 40 | </div> | |
| 60 | - | <div class="import-step hidden" id="import-step-3"> | |
| 61 | - | <h3>3. Preview</h3> | |
| 41 | + | <div class="import-step hidden" id="import-step-preview"> | |
| 42 | + | <h3>2. Preview</h3> | |
| 62 | 43 | <div id="import-preview-container" class="import-preview-container"> | |
| 63 | 44 | <div class="loading">Loading preview...</div> | |
| 64 | 45 | </div> | |
| @@ -72,74 +53,18 @@ | |||
| 72 | 53 | </div> | |
| 73 | 54 | `; | |
| 74 | 55 | ||
| 75 | - | GoingsOn.ui.openModal('Import Data', content); | |
| 56 | + | GoingsOn.ui.openModal('Import from CSV', content); | |
| 76 | 57 | } | |
| 77 | 58 | ||
| 78 | 59 | /** | |
| 79 | - | * Render a plugin option button for the import wizard. | |
| 80 | - | * @param {Object} plugin - Plugin object with id, name, description, plugin_type | |
| 81 | - | * @returns {string} HTML string for the plugin option | |
| 82 | - | */ | |
| 83 | - | function renderPluginOption(plugin) { | |
| 84 | - | const config = plugin.plugin_type?.import || {}; | |
| 85 | - | const extensions = config.file_extensions || []; | |
| 86 | - | const entityTypes = config.entity_types || []; | |
| 87 | - | ||
| 88 | - | return ` | |
| 89 | - | <button class="plugin-option" data-plugin-id="${escAttr(plugin.id)}" onclick="GoingsOn.import.selectPlugin('${escAttr(plugin.id)}')"> | |
| 90 | - | <span class="plugin-name">${esc(plugin.name)}</span> | |
| 91 | - | <span class="plugin-meta"> | |
| 92 | - | ${extensions.length > 0 ? `<span class="plugin-extensions">.${extensions.join(', .')}</span>` : ''} | |
| 93 | - | ${entityTypes.length > 0 ? `<span class="plugin-types">${entityTypes.join(', ')}</span>` : ''} | |
| 94 | - | </span> | |
| 95 | - | <span class="plugin-description">${esc(plugin.description)}</span> | |
| 96 | - | </button> | |
| 97 | - | `; | |
| 98 | - | } | |
| 99 | - | ||
| 100 | - | /** | |
| 101 | - | * Select a plugin in the import wizard and advance to file selection. | |
| 102 | - | * @param {string} pluginId - Plugin ID to select | |
| 103 | - | */ | |
| 104 | - | function selectPlugin(pluginId) { | |
| 105 | - | selectedPlugin = availablePlugins.find(p => p.id === pluginId); | |
| 106 | - | if (!selectedPlugin) return; | |
| 107 | - | ||
| 108 | - | // Update UI - highlight selected plugin | |
| 109 | - | document.querySelectorAll('.plugin-option').forEach(el => { | |
| 110 | - | el.classList.remove('selected'); | |
| 111 | - | }); | |
| 112 | - | document.querySelector(`.plugin-option[data-plugin-id="${pluginId}"]`)?.classList.add('selected'); | |
| 113 | - | ||
| 114 | - | // Show step 2 | |
| 115 | - | document.getElementById('import-step-2').classList.remove('hidden'); | |
| 116 | - | ||
| 117 | - | // Reset file selection | |
| 118 | - | selectedFilePath = null; | |
| 119 | - | previewData = null; | |
| 120 | - | document.getElementById('selected-file-name').textContent = ''; | |
| 121 | - | document.getElementById('import-step-3').classList.add('hidden'); | |
| 122 | - | document.getElementById('import-confirm-btn').disabled = true; | |
| 123 | - | } | |
| 124 | - | ||
| 125 | - | /** | |
| 126 | - | * Opens the file dialog to select a file. | |
| 60 | + | * Opens the file dialog, then loads a preview. | |
| 127 | 61 | */ | |
| 128 | 62 | async function selectFile() { | |
| 129 | - | if (!selectedPlugin) return; | |
| 130 | - | ||
| 131 | - | const config = selectedPlugin.plugin_type?.import || {}; | |
| 132 | - | const extensions = config.file_extensions || ['*']; | |
| 133 | - | ||
| 134 | 63 | try { | |
| 135 | - | // Use Tauri's file dialog | |
| 136 | 64 | const { open } = window.__TAURI__.dialog; | |
| 137 | 65 | const filePath = await open({ | |
| 138 | 66 | multiple: false, | |
| 139 | - | filters: [{ | |
| 140 | - | name: selectedPlugin.name, | |
| 141 | - | extensions: extensions, | |
| 142 | - | }], | |
| 67 | + | filters: [{ name: 'CSV / TSV', extensions: ['csv', 'tsv'] }], | |
| 143 | 68 | }); | |
| 144 | 69 | ||
| 145 | 70 | if (!filePath) return; // User cancelled | |
| @@ -147,8 +72,7 @@ | |||
| 147 | 72 | selectedFilePath = filePath; | |
| 148 | 73 | document.getElementById('selected-file-name').textContent = getFileName(filePath); | |
| 149 | 74 | ||
| 150 | - | // Show step 3 and load preview | |
| 151 | - | document.getElementById('import-step-3').classList.remove('hidden'); | |
| 75 | + | document.getElementById('import-step-preview').classList.remove('hidden'); | |
| 152 | 76 | await loadPreview(); | |
| 153 | 77 | } catch (err) { | |
| 154 | 78 | GoingsOn.ui.showToast('Failed to select file: ' + GoingsOn.utils.getErrorMessage(err), 'error'); | |
| @@ -170,10 +94,14 @@ | |||
| 170 | 94 | container.innerHTML = '<div class="loading">Parsing file...</div>'; | |
| 171 | 95 | ||
| 172 | 96 | try { | |
| 173 | - | previewData = await GoingsOn.api.plugins.preview(selectedPlugin.id, selectedFilePath, {}); | |
| 97 | + | previewData = await GoingsOn.api.import.preview(selectedFilePath); | |
| 174 | 98 | ||
| 175 | 99 | if (!previewData.items || previewData.items.length === 0) { | |
| 176 | - | container.innerHTML = '<p class="import-empty">No items found in file.</p>'; | |
| 100 | + | const warnings = previewData.warnings || []; | |
| 101 | + | container.innerHTML = `<p class="import-empty">No items found in file.</p>`; | |
| 102 | + | if (warnings.length > 0) { | |
| 103 | + | container.insertAdjacentHTML('beforeend', renderWarnings(warnings)); | |
| 104 | + | } | |
| 177 | 105 | document.getElementById('import-confirm-btn').disabled = true; | |
| 178 | 106 | return; | |
| 179 | 107 | } | |
| @@ -181,15 +109,8 @@ | |||
| 181 | 109 | container.innerHTML = renderPreviewTable(previewData); | |
| 182 | 110 | document.getElementById('import-confirm-btn').disabled = false; | |
| 183 | 111 | ||
| 184 | - | // Show warnings if any | |
| 185 | 112 | if (previewData.warnings && previewData.warnings.length > 0) { | |
| 186 | - | const warningsHtml = ` | |
| 187 | - | <div class="import-warnings"> | |
| 188 | - | <strong>Warnings:</strong> | |
| 189 | - | <ul>${previewData.warnings.map(w => `<li>${esc(w)}</li>`).join('')}</ul> | |
| 190 | - | </div> | |
| 191 | - | `; | |
| 192 | - | container.insertAdjacentHTML('beforeend', warningsHtml); | |
| 113 | + | container.insertAdjacentHTML('beforeend', renderWarnings(previewData.warnings)); | |
| 193 | 114 | } | |
| 194 | 115 | } catch (err) { | |
| 195 | 116 | container.innerHTML = `<p class="import-error">Failed to parse file: ${esc(GoingsOn.utils.getErrorMessage(err))}</p>`; | |
| @@ -197,29 +118,37 @@ | |||
| 197 | 118 | } | |
| 198 | 119 | } | |
| 199 | 120 | ||
| 121 | + | function renderWarnings(warnings) { | |
| 122 | + | return ` | |
| 123 | + | <div class="import-warnings"> | |
| 124 | + | <strong>Warnings:</strong> | |
| 125 | + | <ul>${warnings.map(w => `<li>${esc(w)}</li>`).join('')}</ul> | |
| 126 | + | </div> | |
| 127 | + | `; | |
| 128 | + | } | |
| 129 | + | ||
| 200 | 130 | /** | |
| 201 | 131 | * Renders the preview table. | |
| 202 | 132 | */ | |
| 203 | 133 | function renderPreviewTable(data) { | |
| 204 | - | const entityType = data.entity_type || 'item'; | |
| 134 | + | const entityType = data.entityType || 'item'; | |
| 205 | 135 | const items = data.items || []; | |
| 206 | 136 | const maxPreview = 25; | |
| 207 | 137 | const displayItems = items.slice(0, maxPreview); | |
| 208 | 138 | ||
| 209 | - | // Determine columns based on entity type | |
| 210 | 139 | const columns = getColumnsForEntityType(entityType); | |
| 211 | 140 | ||
| 212 | 141 | return ` | |
| 213 | 142 | <p class="import-summary"> | |
| 214 | - | <strong>${items.length}</strong> ${entityType}${items.length !== 1 ? 's' : ''} found | |
| 143 | + | <strong>${items.length}</strong> ${esc(entityType)}${items.length !== 1 ? 's' : ''} found | |
| 215 | 144 | </p> | |
| 216 | 145 | <div class="import-preview-table-wrapper"> | |
| 217 | 146 | <table class="data-table import-preview-table"> | |
| 218 | 147 | <thead> | |
| 219 | - | <tr>${columns.map(c => `<th>${c.label}</th>`).join('')}</tr> | |
| 148 | + | <tr>${columns.map(c => `<th>${esc(c.label)}</th>`).join('')}</tr> | |
| 220 | 149 | </thead> | |
| 221 | 150 | <tbody> | |
| 222 | - | ${displayItems.map((item, idx) => renderPreviewRow(item, columns, idx)).join('')} | |
| 151 | + | ${displayItems.map(item => renderPreviewRow(item, columns)).join('')} | |
| 223 | 152 | </tbody> | |
| 224 | 153 | </table> | |
| 225 | 154 | </div> | |
| @@ -228,14 +157,15 @@ | |||
| 228 | 157 | } | |
| 229 | 158 | ||
| 230 | 159 | /** | |
| 231 | - | * Gets the columns to display based on entity type. | |
| 160 | + | * Columns to display per entity type. Keys are the camelCase fields the | |
| 161 | + | * native importer emits on each item's `data` object. | |
| 232 | 162 | */ | |
| 233 | 163 | function getColumnsForEntityType(entityType) { | |
| 234 | 164 | switch (entityType) { | |
| 235 | 165 | case 'task': | |
| 236 | 166 | return [ | |
| 237 | 167 | { key: 'description', label: 'Description' }, | |
| 238 | - | { key: 'project_name', label: 'Project' }, | |
| 168 | + | { key: 'projectName', label: 'Project' }, | |
| 239 | 169 | { key: 'priority', label: 'Priority' }, | |
| 240 | 170 | { key: 'due', label: 'Due' }, | |
| 241 | 171 | ]; | |
| @@ -243,7 +173,7 @@ | |||
| 243 | 173 | return [ | |
| 244 | 174 | { key: 'name', label: 'Name' }, | |
| 245 | 175 | { key: 'description', label: 'Description' }, | |
| 246 | - | { key: 'project_type', label: 'Type' }, | |
| 176 | + | { key: 'projectType', label: 'Type' }, | |
| 247 | 177 | { key: 'status', label: 'Status' }, | |
| 248 | 178 | ]; | |
| 249 | 179 | case 'event': | |
| @@ -254,16 +184,14 @@ | |||
| 254 | 184 | { key: 'location', label: 'Location' }, | |
| 255 | 185 | ]; | |
| 256 | 186 | default: | |
| 257 | - | return [ | |
| 258 | - | { key: 'description', label: 'Description' }, | |
| 259 | - | ]; | |
| 187 | + | return [{ key: 'description', label: 'Description' }]; | |
| 260 | 188 | } | |
| 261 | 189 | } | |
| 262 | 190 | ||
| 263 | 191 | /** | |
| 264 | 192 | * Renders a preview table row. | |
| 265 | 193 | */ | |
| 266 | - | function renderPreviewRow(item, columns, index) { | |
| 194 | + | function renderPreviewRow(item, columns) { | |
| 267 | 195 | const data = item.data || {}; | |
| 268 | 196 | return ` | |
| 269 | 197 | <tr> | |
| @@ -275,9 +203,6 @@ | |||
| 275 | 203 | `; | |
| 276 | 204 | } | |
| 277 | 205 | ||
| 278 | - | /** | |
| 279 | - | * Truncates a string to a maximum length. | |
| 280 | - | */ | |
| 281 | 206 | function truncate(str, maxLen) { | |
| 282 | 207 | if (!str) return ''; | |
| 283 | 208 | str = String(str); | |
| @@ -288,25 +213,24 @@ | |||
| 288 | 213 | * Executes the import. | |
| 289 | 214 | */ | |
| 290 | 215 | async function executeImport() { | |
| 291 | - | if (!selectedPlugin || !selectedFilePath || !previewData) return; | |
| 216 | + | if (!selectedFilePath || !previewData) return; | |
| 292 | 217 | ||
| 293 | 218 | const btn = document.getElementById('import-confirm-btn'); | |
| 294 | 219 | btn.disabled = true; | |
| 295 | 220 | btn.textContent = 'Importing...'; | |
| 296 | 221 | ||
| 297 | 222 | try { | |
| 298 | - | const result = await GoingsOn.api.plugins.execute(selectedPlugin.id, selectedFilePath, {}); | |
| 223 | + | const result = await GoingsOn.api.import.execute(selectedFilePath); | |
| 299 | 224 | ||
| 300 | 225 | GoingsOn.ui.closeModal(); | |
| 301 | 226 | ||
| 302 | - | const entityType = previewData.entity_type || 'item'; | |
| 303 | - | if (result.failed_count > 0) { | |
| 304 | - | GoingsOn.ui.showToast(`Imported ${result.imported_count} ${entityType}(s), ${result.failed_count} failed`, 'warning'); | |
| 227 | + | const entityType = previewData.entityType || 'item'; | |
| 228 | + | if (result.failedCount > 0) { | |
| 229 | + | GoingsOn.ui.showToast(`Imported ${result.importedCount} ${entityType}(s), ${result.failedCount} failed`, 'warning'); | |
| 305 | 230 | } else { | |
| 306 | - | GoingsOn.ui.showToast(`Successfully imported ${result.imported_count} ${entityType}(s)!`, 'success'); | |
| 231 | + | GoingsOn.ui.showToast(`Successfully imported ${result.importedCount} ${entityType}(s)!`, 'success'); | |
| 307 | 232 | } | |
| 308 | 233 | ||
| 309 | - | // Refresh the current view | |
| 310 | 234 | if (typeof GoingsOn.navigation !== 'undefined' && GoingsOn.navigation.reloadCurrentView) { | |
| 311 | 235 | GoingsOn.navigation.reloadCurrentView(); | |
| 312 | 236 | } | |
| @@ -321,7 +245,6 @@ | |||
| 321 | 245 | ||
| 322 | 246 | GoingsOn.import = { | |
| 323 | 247 | openModal: openImportModal, | |
| 324 | - | selectPlugin, | |
| 325 | 248 | selectFile, | |
| 326 | 249 | executeImport, | |
| 327 | 250 | }; |