diff --git a/Cargo.lock b/Cargo.lock index 9448d79..633a449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,9 +247,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "apply" @@ -801,9 +801,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.14" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "jobserver", "libc", @@ -1055,7 +1055,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1077,7 +1077,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "quote", "syn 1.0.109", @@ -1143,7 +1143,7 @@ dependencies = [ [[package]] name = "cosmic-theme" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "almost", "cosmic-config", @@ -1183,6 +1183,7 @@ dependencies = [ "serde_json", "sqlx", "tokio", + "urlencoding", "vergen", ] @@ -1498,9 +1499,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" dependencies = [ "litrs", ] @@ -1644,9 +1645,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" @@ -2307,9 +2308,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" dependencies = [ "atomic-waker", "bytes", @@ -2650,7 +2651,7 @@ dependencies = [ [[package]] name = "iced" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "dnd", "iced_accessibility", @@ -2668,7 +2669,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "accesskit", "accesskit_winit", @@ -2677,7 +2678,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "bitflags 2.8.0", "bytes", @@ -2702,7 +2703,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "futures", "iced_core", @@ -2728,7 +2729,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "bitflags 2.8.0", "bytemuck", @@ -2750,7 +2751,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -2762,7 +2763,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "bytes", "cosmic-client-toolkit", @@ -2778,7 +2779,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "bytemuck", "cosmic-text", @@ -2794,7 +2795,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "as-raw-xcb-connection", "bitflags 2.8.0", @@ -2825,7 +2826,7 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "cosmic-client-toolkit", "dnd", @@ -2844,7 +2845,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.14.0-dev" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "cosmic-client-toolkit", "dnd", @@ -3330,7 +3331,7 @@ checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libcosmic" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#0b7e23444afb3f351cd947c52babb6b87f30381d" +source = "git+https://github.com/pop-os/libcosmic#8a0e74b189f53dae9b8001c6fa5cf1820b3ececb" dependencies = [ "apply", "ashpd 0.9.2", @@ -3398,7 +3399,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.8.0", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", ] [[package]] @@ -3478,9 +3479,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "lru" @@ -3632,9 +3633,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", "simd-adler32", @@ -3692,9 +3693,9 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ "libc", "log", @@ -4143,9 +4144,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.70" +version = "0.10.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -4175,9 +4176,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" dependencies = [ "cc", "libc", @@ -4317,7 +4318,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", "smallvec", "windows-targets 0.52.6", ] @@ -4713,9 +4714,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -4869,9 +4870,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.9" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75ec5e92c4d8aede845126adc388046234541629e76029599ed35a003c7ed24" +checksum = "d34b5020fcdea098ef7d95e9f89ec15952123a4a039badd09fabebe9e963e839" dependencies = [ "cc", "cfg-if", @@ -5162,18 +5163,18 @@ checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -5182,9 +5183,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" dependencies = [ "indexmap", "itoa", @@ -5286,9 +5287,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "skrifa" -version = "0.26.5" +version = "0.26.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92bf3f3af711d696eff796a4f28136927d40eb8108002b6f7919dc0cee27a5d" +checksum = "8cc1aa86c26dbb1b63875a7180aa0819709b33348eb5b1491e4321fae388179d" dependencies = [ "bytemuck", "read-fonts", @@ -5314,9 +5315,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" dependencies = [ "serde", ] @@ -5800,9 +5801,9 @@ checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72" [[package]] name = "tempfile" -version = "3.16.0" +version = "3.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +checksum = "22e5a0acb1f3f55f65cc4a866c361b2fb2a0ff6366785ae6fbb5f85df07ba230" dependencies = [ "cfg-if", "fastrand 2.3.0", @@ -6066,7 +6067,7 @@ checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "toml_datetime", - "winnow 0.7.2", + "winnow 0.7.3", ] [[package]] @@ -6163,9 +6164,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "uds_windows" @@ -6217,9 +6218,9 @@ checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-linebreak" @@ -6759,7 +6760,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.5.8", + "redox_syscall 0.5.9", "wasite", ] @@ -7193,9 +7194,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index c3360cd..687dc2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ serde = "1.0.210" serde_json = "1.0.128" sqlx = { version = "0.8.2", features = ["sqlite", "runtime-tokio", "chrono"] } tokio = { version = "1.40.0", features = ["full"] } +urlencoding = "2.1.3" [dependencies.i18n-embed] version = "0.15" diff --git a/i18n/en/cosmicding.ftl b/i18n/en/cosmicding.ftl index b73763b..9d11ef7 100644 --- a/i18n/en/cosmicding.ftl +++ b/i18n/en/cosmicding.ftl @@ -81,7 +81,7 @@ token = Token unexpected-http-return-code = Unexpected HTTP return code {$http_rc} unread = Unread updated-account = Updated account {$acc} -updated-bookmark-in-account = Updated bookmark in account {$acc} +updated-bookmark-in-account = Updated bookmark {$bkmrk} in account {$acc} url = URL view = View yes = Yes diff --git a/i18n/sv/cosmicding.ftl b/i18n/sv/cosmicding.ftl index 94cd94e..497dc32 100644 --- a/i18n/sv/cosmicding.ftl +++ b/i18n/sv/cosmicding.ftl @@ -81,7 +81,7 @@ token = Token unexpected-http-return-code = Oväntad HTTP-returkod {$http_rc} unread = Oläst updated-account = Uppdaterat konto {$acc} -updated-bookmark-in-account = Uppdaterat bokmärke på kontot {$acc} +updated-bookmark-in-account = Uppdaterat bokmärke {$bkmrk} på kontot {$acc} url = URL view = Visa yes = Ja diff --git a/src/app.rs b/src/app.rs index 7dbb7c6..ffde03c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -938,20 +938,34 @@ impl Application for Cosmicding { } Message::AddBookmark(account, bookmark) => { let mut new_bkmrk: Option = None; + let mut bookmark_exists = false; if let Some(ref mut database) = &mut self.bookmarks_cursor.database { block_on(async { match http::add_bookmark(&account, &bookmark).await { Ok(value) => { - new_bkmrk = Some(value); - commands.push( - self.toasts - .push(widget::toaster::Toast::new(fl!( - "added-bookmark-to-account", - bkmrk = bookmark.url.clone(), - acc = account.display_name.clone() - ))) - .map(cosmic::app::Message::App), - ); + new_bkmrk = Some(value.bookmark); + if value.is_new { + commands.push( + self.toasts + .push(widget::toaster::Toast::new(fl!( + "added-bookmark-to-account", + bkmrk = bookmark.url.clone(), + acc = account.display_name.clone() + ))) + .map(cosmic::app::Message::App), + ); + } else { + bookmark_exists = true; + commands.push( + self.toasts + .push(widget::toaster::Toast::new(fl!( + "updated-bookmark-in-account", + bkmrk = bookmark.url, + acc = account.display_name.clone() + ))) + .map(cosmic::app::Message::App), + ); + } } Err(e) => { log::error!("Error adding bookmark: {}", e); @@ -964,15 +978,18 @@ impl Application for Cosmicding { } }); if let Some(bkmrk) = new_bkmrk { - block_on(async { - db::SqliteDatabase::add_bookmark(database, &bkmrk).await; - }); + if bookmark_exists { + block_on(async { + db::SqliteDatabase::update_bookmark(database, &bkmrk, &bkmrk).await; + }); + } else { + block_on(async { + db::SqliteDatabase::add_bookmark(database, &bkmrk).await; + }); + } commands.push(self.update(Message::LoadBookmarks)); } }; - block_on(async { - self.bookmarks_cursor.refresh_count().await; - }); self.core.window.show_context = false; } Message::RemoveBookmark(account_id, bookmark) => { @@ -1013,10 +1030,7 @@ impl Application for Cosmicding { db::SqliteDatabase::delete_bookmark(database, &bookmark).await; }); } - block_on(async { - self.bookmarks_cursor.refresh_count().await; - self.bookmarks_cursor.fetch_next_results().await; - }); + commands.push(self.update(Message::LoadBookmarks)); self.bookmarks_view.bookmarks = self.bookmarks_cursor.result.clone().unwrap(); self.core.window.show_context = false; } @@ -1048,6 +1062,7 @@ impl Application for Cosmicding { self.toasts .push(widget::toaster::Toast::new(fl!( "updated-bookmark-in-account", + bkmrk = bookmark.url.clone(), acc = account.display_name.clone() ))) .map(cosmic::app::Message::App), @@ -1071,7 +1086,7 @@ impl Application for Cosmicding { .position(|x| x.id == bookmark.id) .unwrap(); block_on(async { - db::SqliteDatabase::update_bookmark(database, &bookmark, &bkmrk).await; + db::SqliteDatabase::update_bookmark(database, &bkmrk, &bkmrk).await; }); self.bookmarks_view.bookmarks[index] = bkmrk; } diff --git a/src/db/mod.rs b/src/db/mod.rs index 1254a41..49bb014 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -328,7 +328,7 @@ impl SqliteDatabase { .bind(&new_bookmark.date_modified) .bind(&new_bookmark.website_title) .bind(&new_bookmark.website_description) - .bind(old_bookmark.id) + .bind(old_bookmark.linkding_internal_id) .execute(&self.conn) .await .unwrap(); diff --git a/src/http/mod.rs b/src/http/mod.rs index b587288..da01b6f 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -1,6 +1,10 @@ use crate::fl; use crate::models::account::{Account, LinkdingAccountApiResponse}; -use crate::models::bookmarks::{Bookmark, DetailedResponse, LinkdingBookmarksApiResponse}; +use crate::models::bookmarks::{ + Bookmark, CheckDetailsResponse, DetailedResponse, LinkdingBookmarksApiCheckResponse, + LinkdingBookmarksApiResponse, +}; +use crate::utils::json::parse_serde_json_value_to_raw_string; use anyhow::Result; use chrono::{DateTime, Utc}; use reqwest::{ @@ -10,6 +14,7 @@ use reqwest::{ use serde_json::Value; use std::fmt::Write; use std::time::{SystemTime, UNIX_EPOCH}; +use urlencoding::encode; pub async fn fetch_bookmarks_from_all_accounts(accounts: Vec) -> Vec { let mut all_responses: Vec = Vec::new(); @@ -175,10 +180,11 @@ pub async fn fetch_bookmarks_for_account( Ok(detailed_response) } +#[allow(clippy::too_many_lines)] pub async fn add_bookmark( account: &Account, bookmark: &Bookmark, -) -> Result> { +) -> Result> { let rest_api_url: String = account.instance.clone() + "/api/bookmarks/"; let mut headers = HeaderMap::new(); let http_client = ClientBuilder::new() @@ -202,33 +208,95 @@ pub async fn add_bookmark( obj.remove("date_added"); obj.remove("date_modified"); } - let response: reqwest::Response = http_client - .post(rest_api_url) - .headers(headers) - .json(&transformed_json_value) - .send() - .await?; + // NOTE: (vkhitrin) I was not able to get serde_json::value:RawValue to omit quotes + //let bookmark_url = transformed_json_value["url"].to_string().replace('"', ""); + let bookmark_url = + parse_serde_json_value_to_raw_string(transformed_json_value.get("url").unwrap()); + match check_bookmark_on_instance(account, bookmark_url.to_string()).await { + Ok(check) => { + let metadata = check.metadata; + if check.bookmark.is_some() { + let mut bkmrk = check.bookmark.unwrap(); + bkmrk.linkding_internal_id = bkmrk.id; + bkmrk.user_account_id = account.id; + bkmrk.id = None; + if let Some(obj) = transformed_json_value.as_object() { + bkmrk.title = match parse_serde_json_value_to_raw_string( + transformed_json_value.get("title").unwrap(), + ) { + ref s if !s.is_empty() => s.to_string(), + _ => metadata.title.unwrap(), + }; + bkmrk.description = match parse_serde_json_value_to_raw_string( + transformed_json_value.get("description").unwrap(), + ) { + ref s if !s.is_empty() => s.to_string(), + _ => metadata.description.unwrap_or_default(), + }; + bkmrk.notes = match parse_serde_json_value_to_raw_string( + transformed_json_value.get("notes").unwrap(), + ) { + ref s if !s.is_empty() => s.to_string(), + _ => String::new(), + }; + bkmrk.tag_names = if let Value::Array(arr) = &obj["tag_names"] { + let tags: Vec = arr + .iter() + .filter_map(|item| item.as_str().map(std::string::ToString::to_string)) + .collect(); + tags + } else { + Vec::new() + } + } + match edit_bookmark(account, &bkmrk).await { + Ok(value) => Ok(CheckDetailsResponse { + bookmark: value, + is_new: false, + }), + Err(_e) => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + fl!("failed-to-parse-response"), + ))), + } + } else { + let response: reqwest::Response = http_client + .post(rest_api_url) + .headers(headers) + .json(&transformed_json_value) + .send() + .await?; - match response.status() { - StatusCode::CREATED => match response.json::().await { - Ok(mut value) => { - value.linkding_internal_id = value.id; - value.user_account_id = account.id; - value.id = None; - Ok(value) + match response.status() { + StatusCode::CREATED => match response.json::().await { + Ok(mut value) => { + value.linkding_internal_id = value.id; + value.user_account_id = account.id; + value.id = None; + Ok(CheckDetailsResponse { + bookmark: value, + is_new: true, + }) + } + Err(_e) => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + fl!("failed-to-parse-response"), + ))), + }, + status => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + fl!( + "http-error", + http_rc = status.to_string(), + http_err = response.text().await.unwrap() + ), + ))), + } } - Err(_e) => Err(Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - fl!("failed-to-parse-response"), - ))), - }, - status => Err(Box::new(std::io::Error::new( + } + Err(_e) => Err(Box::new(std::io::Error::new( std::io::ErrorKind::Other, - fl!( - "http-error", - http_rc = status.to_string(), - http_err = response.text().await.unwrap() - ), + fl!("failed-to-parse-response"), ))), } } @@ -392,3 +460,50 @@ pub async fn check_account_on_instance( ))), } } + +pub async fn check_bookmark_on_instance( + account: &Account, + url: String, +) -> Result> { + let mut rest_api_url: String = String::new(); + let encoded_bookmark_url = encode(&url); + write!( + &mut rest_api_url, + "{}/api/bookmarks/check/?url={}", + account.instance, encoded_bookmark_url + ) + .unwrap(); + let mut headers = HeaderMap::new(); + let http_client = ClientBuilder::new() + .danger_accept_invalid_certs(account.tls) + .build()?; + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Token {}", account.api_token)).unwrap(), + ); + let response: reqwest::Response = http_client + .get(rest_api_url) + .headers(headers) + .send() + .await?; + match response.status() { + StatusCode::OK => match response.json::().await { + Ok(value) => Ok(value), + Err(_e) => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + fl!("failed-to-find-linkding-api-endpoint"), + ))), + }, + StatusCode::UNAUTHORIZED => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + fl!("invalid-api-token"), + ))), + _ => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + fl!( + "unexpected-http-return-code", + http_rc = response.status().to_string() + ), + ))), + } +} diff --git a/src/main.rs b/src/main.rs index 7eb185c..4abf557 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod http; mod models; mod pages; mod style; +mod utils; use core::settings; diff --git a/src/models/bookmarks.rs b/src/models/bookmarks.rs index afaae2c..53b7f9f 100644 --- a/src/models/bookmarks.rs +++ b/src/models/bookmarks.rs @@ -98,3 +98,24 @@ impl DetailedResponse { } } } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LinkdingBookmarksApiCheckMetadata { + pub url: Option, + pub title: Option, + pub description: Option, + pub preview_image: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LinkdingBookmarksApiCheckResponse { + pub bookmark: Option, + pub metadata: LinkdingBookmarksApiCheckMetadata, + pub auto_tags: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct CheckDetailsResponse { + pub bookmark: Bookmark, + pub is_new: bool, +} diff --git a/src/utils/json.rs b/src/utils/json.rs new file mode 100644 index 0000000..b1510e5 --- /dev/null +++ b/src/utils/json.rs @@ -0,0 +1,21 @@ +use serde_json::Value; + +// NOTE: I was not able to correctly parse serde_json::Value into a raw value +pub fn parse_serde_json_value_to_raw_string(v: &Value) -> String { + let mut parsed_string = v.to_string(); + // Trim leading double quote + if parsed_string.starts_with('"') { + parsed_string.remove(0); + } + // Trim trailing double quote + if parsed_string.ends_with('"') { + parsed_string.pop(); + } + // Reset string if escaped newline provided + // This can occur when clicking on a cosmic::widget::text_editor widget + if parsed_string == "\\n" { + parsed_string = String::new(); + } + // "Unescape" escaped new lines + parsed_string.replace("\\n", "\n") +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs new file mode 100644 index 0000000..22fdbb3 --- /dev/null +++ b/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod json;