diff --git a/.gitignore b/.gitignore index 3d903545..5a0c6f67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ +## Cargo generated directory /target -.vscode +## cargo-packager generated directory +/dist +.vscode .DS_Store ## Temp hack to support login on mobile platforms. diff --git a/Cargo.lock b/Cargo.lock index 18306a9d..00aa9dc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -77,6 +77,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-build" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a133d38cebf328adaea4bc1891d9568e14a394b50e4f4ba5f63dc14e8beaaee9" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -328,9 +337,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ "serde", ] @@ -391,6 +400,15 @@ dependencies = [ "objc2", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + [[package]] name = "bs58" version = "0.5.0" @@ -1360,7 +1378,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642" dependencies = [ - "block2", + "block2 0.4.0", "objc2", ] @@ -1603,7 +1621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -1612,7 +1630,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "libc", ] @@ -1723,7 +1741,7 @@ dependencies = [ [[package]] name = "makepad-derive-live" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-micro-proc-macro", @@ -1732,7 +1750,7 @@ dependencies = [ [[package]] name = "makepad-derive-wasm-bridge" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-micro-proc-macro", ] @@ -1740,7 +1758,7 @@ dependencies = [ [[package]] name = "makepad-derive-widget" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-micro-proc-macro", @@ -1749,7 +1767,7 @@ dependencies = [ [[package]] name = "makepad-draw" version = "0.6.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "ab_glyph_rasterizer", "fxhash", @@ -1766,17 +1784,17 @@ dependencies = [ [[package]] name = "makepad-futures" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-futures-legacy" version = "0.7.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-html" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", ] @@ -1784,7 +1802,7 @@ dependencies = [ [[package]] name = "makepad-http" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-jni-sys" @@ -1795,7 +1813,7 @@ checksum = "9775cbec5fa0647500c3e5de7c850280a88335d1d2d770e5aa2332b801ba7064" [[package]] name = "makepad-live-compiler" version = "0.5.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-derive-live", "makepad-live-tokenizer", @@ -1805,7 +1823,7 @@ dependencies = [ [[package]] name = "makepad-live-id" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id-macros", ] @@ -1813,7 +1831,7 @@ dependencies = [ [[package]] name = "makepad-live-id-macros" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-micro-proc-macro", ] @@ -1821,7 +1839,7 @@ dependencies = [ [[package]] name = "makepad-live-tokenizer" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-math", @@ -1831,7 +1849,7 @@ dependencies = [ [[package]] name = "makepad-markdown" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", ] @@ -1839,17 +1857,17 @@ dependencies = [ [[package]] name = "makepad-math" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-micro-proc-macro" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-micro-serde" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-id", "makepad-micro-serde-derive", @@ -1858,7 +1876,7 @@ dependencies = [ [[package]] name = "makepad-micro-serde-derive" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-micro-proc-macro", ] @@ -1866,12 +1884,12 @@ dependencies = [ [[package]] name = "makepad-objc-sys" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "makepad-platform" version = "0.6.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "hilog-sys", "makepad-android-state", @@ -1893,7 +1911,7 @@ dependencies = [ [[package]] name = "makepad-rustybuzz" version = "0.8.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "bitflags 1.3.2", "bytemuck", @@ -1908,7 +1926,7 @@ dependencies = [ [[package]] name = "makepad-shader-compiler" version = "0.5.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-live-compiler", ] @@ -1916,7 +1934,7 @@ dependencies = [ [[package]] name = "makepad-vector" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "resvg", "ttf-parser", @@ -1925,7 +1943,7 @@ dependencies = [ [[package]] name = "makepad-wasm-bridge" version = "0.4.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-derive-wasm-bridge", "makepad-live-id", @@ -1934,7 +1952,7 @@ dependencies = [ [[package]] name = "makepad-widgets" version = "0.6.0" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-derive-widget", "makepad-draw", @@ -1948,24 +1966,24 @@ dependencies = [ [[package]] name = "makepad-windows" version = "0.51.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ - "windows-core 0.51.1 (git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content)", + "windows-core 0.51.1 (git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path)", "windows-targets 0.48.5", ] [[package]] name = "makepad-zune-core" version = "0.2.14" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", ] [[package]] name = "makepad-zune-inflate" version = "0.2.54" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "simd-adler32", ] @@ -1973,7 +1991,7 @@ dependencies = [ [[package]] name = "makepad-zune-jpeg" version = "0.3.17" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-zune-core", ] @@ -1981,7 +1999,7 @@ dependencies = [ [[package]] name = "makepad-zune-png" version = "0.2.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "makepad-zune-core", "makepad-zune-inflate", @@ -2084,7 +2102,7 @@ source = "git+https://github.com/matrix-org/matrix-rust-sdk#dc055c632c106c6392ee dependencies = [ "as_variant", "async-trait", - "bitflags 2.4.1", + "bitflags 2.6.0", "eyeball", "eyeball-im", "futures-util", @@ -2341,7 +2359,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad5a3bbb2ae61f345b8c11776f2e79fc2bb71d1901af9a5f81f03c9238a05d86" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "ctor", "napi-sys-ohos", "once_cell", @@ -2413,12 +2431,47 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2 0.5.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2 0.5.1", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + [[package]] name = "objc2-encode" version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.6.0", + "block2 0.5.1", + "libc", + "objc2", +] + [[package]] name = "object" version = "0.32.1" @@ -2747,7 +2800,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "666f0f59e259aea2d72e6012290c09877a780935cc3c18b1ceded41f3890d59c" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "memchr", "pulldown-cmark-escape", "unicase", @@ -3040,6 +3093,21 @@ dependencies = [ "robius-android-env", ] +[[package]] +name = "robius-location" +version = "0.1.0" +source = "git+https://github.com/project-robius/robius-location#d47bd115f50247b98787efc72236dc65ab9c53bc" +dependencies = [ + "android-build", + "cfg-if", + "jni", + "objc2", + "objc2-core-location", + "objc2-foundation", + "robius-android-env", + "windows", +] + [[package]] name = "robius-open" version = "0.1.0" @@ -3084,6 +3152,7 @@ dependencies = [ "rand", "rangemap", "robius-directories", + "robius-location", "robius-open", "robius-use-makepad", "serde", @@ -3253,7 +3322,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -3288,7 +3357,7 @@ version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -3967,7 +4036,7 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "ttf-parser" version = "0.21.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" [[package]] name = "typenum" @@ -4361,6 +4430,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.51.1" @@ -4373,22 +4452,65 @@ dependencies = [ [[package]] name = "windows-core" version = "0.51.1" -source = "git+https://github.com/kevinaboos/makepad?branch=modal_sends_dismissed_action_to_inner_modal_content#dd319e6e74d9d70f8c90fef639109ae32caee863" +source = "git+https://github.com/kevinaboos/makepad?branch=apple_bundle_resource_path#7e70055ab2fda14746374d02ca7ae70f804c40de" dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.75", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.75", +] + [[package]] name = "windows-registry" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -4404,7 +4526,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] diff --git a/Cargo.toml b/Cargo.toml index 9deb4084..ed016d84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,13 +17,15 @@ metadata.makepad-auto-version = "zqpv-Yj-K7WNVK2I8h5Okhho46Q=" [dependencies] # makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik" } -makepad-widgets = { git = "https://github.com/kevinaboos/makepad", branch = "modal_sends_dismissed_action_to_inner_modal_content" } +makepad-widgets = { git = "https://github.com/kevinaboos/makepad", branch = "apple_bundle_resource_path" } + ## Including this crate automatically configures all `robius-*` crates to work with Makepad. robius-use-makepad = "0.1.0" robius-open = "0.1.0" ## A fork of the `directories` crate that adds support for Android by using our `robius-android-env` crate. robius-directories = { git = "https://github.com/project-robius/robius-directories", branch = "robius"} +robius-location = { git = "https://github.com/project-robius/robius-location" } anyhow = "1.0" chrono = "0.4" @@ -98,3 +100,99 @@ all-features = true # Temporarily include all debug info even for release builds. [profile.release] debug = "full" + + + + +## Configuration for `cargo packager` +[package.metadata.packager] +product_name = "Robrix" +identifier = "org.robius.robrix" +category = "SocialNetworking" +authors = ["Project Robius ", "Kevin Boos "] +publisher = "robius" +license_file = "LICENSE-MIT" +copyright = "Copyright 2023-2024, Project Robius" +homepage = "https://github.com/project-robius" +### Note: there is an 80-character max for each line of the `long_description`. +long_description = """ +Robrix is a multi-platform Matrix chat client written in pure Rust +using the Makepad UI framework (https://github.com/makepad/makepad) +and the Project Robius app dev framework and platform abstractions +(https://github.com/project-robius). +Robrix runs on all major desktop and mobile platforms: +macOS, Windows, Linux, Android, and iOS. +""" +icons = ["./packaging/robrix_logo_alpha.png"] +out_dir = "./dist" + +## The below command uses cargo-metadata to determine the path of the `makepad_widgets` crate on the host build system, +## and copies the `makepad-widgets/resources` directory to the `./dist/resources/makepad_widgets` directory. +## We also copy the Robrix project's `resources/` directory to the `./dist/resources/robrix` directory. +## +## This is necessary because the `cargo packager` command only supports defining resources at a known path +## (see the below `resources = [...]` block below), +## so we need to copy the resources to a known fixed (static) path before packaging, +## such that cargo-packager can locate them and include them in the final package. +before-packaging-command = """ +cargo run --manifest-path packaging/before-packaging-command/Cargo.toml \ + before-packaging \ + --binary-name robrix \ + --path-to-binary ./target/release/robrix +""" + +## See the above paragraph comments for more info on how we create/populate the below `src` directories. +resources = [ + { src = "./dist/resources/makepad_widgets", target = "makepad_widgets" }, + { src = "./dist/resources/robrix", target = "robrix" }, +] + +## We then build the entire Robrix project and set the `MAKEPAD_PACKAGE_DIR` env var to the proper value. +## * For macOS app bundles, this should be set to `.` because we set the `apple_bundle` cfg option +## for Makepad, which causes Makepad to look for resources in the `Contents/Resources/` directory, +## which is where the resources are located for an Apple app bundle (`.app` and `.dmg`). +## * For Debian `.deb` packages, this should be set to `/usr/lib/`, +## which is currently `/usr/lib/robrix`. +## This is the directory in which `dpkg` copies app resource files to when installing the `.deb` package. +## * On Linux, we also strip the binaries of unneeded content, as required for Debian packages. +## * For Debian and Pacman (still a to-do!) packages, we also auto-generate the list of dependencies required by Robrix. +## +before-each-package-command = """ +cargo run --manifest-path packaging/before-packaging-command/Cargo.toml \ + before-each-package \ + --binary-name robrix \ + --path-to-binary ./target/release/robrix +""" + +deep_link_protocols = [ + { schemes = ["robrix", "matrix"], role = "viewer" }, ## `name` is left as default +] + +[package.metadata.packager.deb] +depends = "./dist/depends_deb.txt" +desktop_template = "./packaging/robrix.desktop" +section = "utils" + +[package.metadata.packager.macos] +minimum_system_version = "11.0" +frameworks = [ ] +info_plist_path = "./packaging/Info.plist" +entitlements = "./packaging/Entitlements.plist" +signing_identity = "Developer ID Application: AppChef Inc. (SFVQ5V48GD)" + + +## Configuration for `cargo packager`'s generation of a macOS `.dmg`. +[package.metadata.packager.dmg] +background = "./packaging/Robrix macOS dmg background.png" +window_size = { width = 960, height = 540 } +app_position = { x = 200, y = 250 } +application_folder_position = { x = 760, y = 250 } + + +## Configuration for `cargo packager`'s generation of a Windows `.exe` setup installer. +[package.metadata.packager.nsis] +## See this: +appdata_paths = [ + "$APPDATA/$PUBLISHER/$PRODUCTNAME", + "$LOCALAPPDATA/$PRODUCTNAME", +] diff --git a/LICENSE-MIT b/LICENSE-MIT index 15120410..87eac651 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2023 Project Robius Developers +Copyright (c) 2023-2024 Project Robius Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/packaging/Entitlements.plist b/packaging/Entitlements.plist new file mode 100644 index 00000000..09fb4c40 --- /dev/null +++ b/packaging/Entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.security.personal-information.location + + + diff --git a/packaging/Info.plist b/packaging/Info.plist new file mode 100644 index 00000000..48031619 --- /dev/null +++ b/packaging/Info.plist @@ -0,0 +1,61 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + Robrix + CFBundleExecutable + robrix + CFBundleIconFile + Robrix.icns + CFBundleIdentifier + org.robius.robrix + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Robrix + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.0.1-pre-alpha + CFBundleVersion + 20241009.225157 + CSResourcesFileMapped + + LSApplicationCategoryType + public.app-category.social-networking + LSMinimumSystemVersion + 11.0 + CFBundleURLTypes + + + CFBundleURLSchemes + + robrix + matrix + + CFBundleURLName + org.robius.robrix robrix + CFBundleTypeRole + Viewer + + + LSRequiresCarbon + + NSHighResolutionCapable + + NSHumanReadableCopyright + Copyright 2023-2024, Project Robius + NSLocationAlwaysAndWhenInUseUsageDescription + Robrix needs permission to share your current location to a Matrix room. + NSLocationWhenInUseUsageDescription + Robrix needs permission to share your current location to a Matrix room. + NSLocationUsageDescription + Robrix needs permission to share your current location to a Matrix room. + NSLocationDefaultAccuracyReduced + + + + \ No newline at end of file diff --git a/packaging/Robrix macOS dmg background.png b/packaging/Robrix macOS dmg background.png new file mode 100644 index 00000000..e5197b7e Binary files /dev/null and b/packaging/Robrix macOS dmg background.png differ diff --git a/packaging/before-packaging-command/.gitignore b/packaging/before-packaging-command/.gitignore new file mode 100644 index 00000000..04e2336d --- /dev/null +++ b/packaging/before-packaging-command/.gitignore @@ -0,0 +1,6 @@ +## Cargo generated directory +/target + +.vscode +.DS_Store + diff --git a/packaging/before-packaging-command/Cargo.lock b/packaging/before-packaging-command/Cargo.lock new file mode 100644 index 00000000..a2f3c86c --- /dev/null +++ b/packaging/before-packaging-command/Cargo.lock @@ -0,0 +1,299 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "before-packaging-command" +version = "0.1.0" +dependencies = [ + "cargo_metadata", + "directories", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "camino" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/packaging/before-packaging-command/Cargo.toml b/packaging/before-packaging-command/Cargo.toml new file mode 100644 index 00000000..87e0f9af --- /dev/null +++ b/packaging/before-packaging-command/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "before-packaging-command" +version = "0.1.0" +edition = "2021" +rust-version = "1.79" ## required by cargo-packager +description = "A small Rust program that is run by cargo-packager's 'before packaging' commands." + +[dependencies] +cargo_metadata = "0.18" +directories = "5.0.1" diff --git a/packaging/before-packaging-command/src/main.rs b/packaging/before-packaging-command/src/main.rs new file mode 100644 index 00000000..74004869 --- /dev/null +++ b/packaging/before-packaging-command/src/main.rs @@ -0,0 +1,460 @@ +//! This small program is invoked by cargo-packager during its before-packaging steps. +//! +//! This program must be run from the root of the project directory, +//! which is also where the cargo-packager command must be invoked from. +//! +//! This program runs in two modes, one for each kind of before-packaging step in cargo-packager. +//! It requires passing in three arguments: +//! 1. An operating mode, either `before-packaging` or `before-each-packager`. +//! * Passing in `before-packaging` specifies that the `before-packaging-command` is being run +//! by cargo-packager, which gets executed only *once* before cargo-packager generates any package bundles. +//! * Passing in `before-each-package` specifies that the `before-each-package-command` is being run +//! by cargo-packager, which gets executed multiple times: once for *each* package that +//! cargo-packager is going to generate. +//! * The environment variable `CARGO_PACKAGER_FORMAT` is set by cargo-packager to +//! the declare which package format is about to be generated, which include the values +//! given here: . +//! * `app`, `dmg`: for macOS. +//! * `deb`, `appimage`, `pacman`: for Linux. +//! * `nsis`: for Windows; `nsis` generates an installer `setup.exe`. +//! * `wix`: (UNSUPPORTED) for Windows; generates an `.msi` installer package. +//! 2. The `--binary-name` argument, which specifies the name of the main binary that cargo-packager +//! is going to package. This is the name of the binary that is generated by your app's main crate. +//! 3. The `--path-to-binary` argument, which specifies the path to the main binary that cargo-packager +//! is going to package. This is the path to the binary that is generated by cargo when compiling +//! your app's main crate. +//! +//! This program uses the `CARGO_PACKAGER_FORMAT` environment variable to determine +//! which specific build commands and configuration options should be used. +//! + +use core::panic; +use std::{ffi::OsStr, fs, path::{Path, PathBuf}, process::{Command, Stdio}}; +use cargo_metadata::MetadataCommand; + +const EMPTY_ARGS: std::iter::Empty<&str> = std::iter::empty::<&str>(); +const EMPTY_ENVS: std::iter::Empty<(&str, &str)> = std::iter::empty::<(&str, &str)>(); + +/// Returns the value of the `MAKEPAD_PACKAGE_DIR` environment variable +/// that must be set for the given package format. +/// +/// * For macOS app bundles, this should be set to the current directory `.` +/// * This only works because we enable the Makepad `apple_bundle` cfg option, +/// which tells Makepad to invoke Apple's `NSBundle` API to retrieve the resource path at runtime. +/// This resource path points to the bundle's `Contents/Resources/` directory. +/// * For AppImage packages, this should be set to a relative path that goes up two parent directories +/// to account for the fact that AppImage binaries run in a simulated `usr/bin/` directory. +/// * Thus, we need to get to the simulated `usr/lib/` directory to find the resources, +/// which currently works out to "./usr/lib/robrix`. +/// * Note that this must be a relative path, not an absolute path. +/// * For Debian `.deb` packages, this should be set to `/usr/lib/`. +/// * This is the directory in which `dpkg` copies app resource files to +/// when a user installs the `.deb` package. +/// * For Windows NSIS packages, this should be set to `.` (the current dir). +/// * This is because the NSIS installer script copies the resources to the same directory +/// as the installed binaries. +fn makepad_package_dir_value(package_format: &str, main_binary_name: &str) -> String { + match package_format { + "app" | "dmg" => format!("."), + "appimage" => format!("./usr/lib/{}", main_binary_name), + "deb" | "pacman" => format!("/usr/lib/{}", main_binary_name), + "nsis" => format!("."), + _other => panic!("Unsupported package format: {}", _other), + } +} + + +fn main() -> std::io::Result<()> { + let mut is_before_packaging = false; + let mut is_before_each_package = false; + let mut main_binary_name = None; + let mut path_to_binary = None; + let mut host_os_opt: Option = None; + + let mut args = std::env::args().peekable(); + while let Some(arg) = args.next() { + if arg.ends_with("before-packaging") || arg.ends_with("before_packaging") { + is_before_packaging = true; + } + if arg.contains("before-each") || arg.contains("before_each") { + is_before_each_package = true; + } + if arg == "--binary-name" { + main_binary_name = Some(args.next().expect("Expected a binary name after '--binary-name'.")); + } + if arg == "--path-to-binary" { + let path = PathBuf::from(args.next().expect("Expected a path after '--path-to-binary'.")); + if path.exists() { + path_to_binary = Some(path); + } else { + panic!("The '--path-to-binary' path does not exist: '{}'", path.display()); + } + } + if host_os_opt.is_none() && (arg.contains("host_os") || arg.contains("host-os")) + { + host_os_opt = arg + .split("=") + .last() + .map(|s| s.to_string()) + .or_else(|| args.peek().map(|s| s.to_string())); + } + } + + let main_binary_name = main_binary_name.expect("Missing required argument '--binary-name'"); + let path_to_binary = path_to_binary.expect("Missing required argument '--path-to-binary'"); + let host_os = host_os_opt.as_deref().unwrap_or(std::env::consts::OS); + + match (is_before_packaging, is_before_each_package) { + (true, false) => before_packaging(host_os, &main_binary_name), + (false, true) => before_each_package(host_os, &main_binary_name, &path_to_binary), + (true, true) => panic!("Cannot run both 'before-packaging' and 'before-each-package' commands at the same time."), + (false, false) => panic!("Please specify either the 'before-packaging' or 'before-each-package' command."), + } +} + +/// This function is run only *once* before cargo-packager generates any package bundles. +/// +/// ## Functionality +/// 1. Creates a directory for the resources to be packaged, which is currently `./dist/resources/`. +/// 2. Recursively copies the resources from the `makepad-widgets` crate to a subdirectory +/// of the directory created in step 1, which is currently `./dist/resources/makepad_widgets/`. +/// The location of the `makepad-widgets` crate is determined using `cargo-metadata`. +/// 3. Recursively copies the app-specific `./resources` directory to `./dist/resources//`. +fn before_packaging(_host_os: &str, main_binary_name: &str) -> std::io::Result<()> { + let cwd = std::env::current_dir()?; + let dist_resources_dir = cwd.join("dist").join("resources"); + fs::create_dir_all(&dist_resources_dir)?; + + let app_resources_dest = dist_resources_dir.join(main_binary_name).join("resources"); + let app_resources_src = cwd.join("resources"); + let makepad_widgets_resources_dest = dist_resources_dir.join("makepad_widgets").join("resources"); + let makepad_widgets_resources_src = { + let cargo_metadata = MetadataCommand::new() + .exec() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + + let makepad_widgets_package = cargo_metadata + .packages + .iter() + .find(|package| package.name == "makepad-widgets") + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "makepad-widgets package not found"))?; + + makepad_widgets_package.manifest_path + .parent() + .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::NotFound, "makepad-widgets package manifest path not found"))? + .join("resources") + }; + + /// Copy files from source to destination recursively. + fn copy_recursively(source: impl AsRef, destination: impl AsRef) -> std::io::Result<()> { + fs::create_dir_all(&destination)?; + for entry in fs::read_dir(source)? { + let entry = entry?; + let filetype = entry.file_type()?; + if filetype.is_dir() { + copy_recursively(entry.path(), destination.as_ref().join(entry.file_name()))?; + } else { + fs::copy(entry.path(), destination.as_ref().join(entry.file_name()))?; + } + } + Ok(()) + } + + println!("Copying makepad-widgets resources...\n --> From: {}\n to: {}", makepad_widgets_resources_src.as_std_path().display(), makepad_widgets_resources_dest.display()); + copy_recursively(&makepad_widgets_resources_src, &makepad_widgets_resources_dest)?; + println!(" --> Done!"); + println!("Copying app-specific resources...\n --> From {}\n to: {}", app_resources_src.display(), app_resources_dest.display()); + copy_recursively(&app_resources_src, &app_resources_dest)?; + println!(" --> Done!"); + + Ok(()) +} + + +/// The function that is run by cargo-packager's `before-each-package-command`. +/// +/// It's just a simple wrapper that invokes the function for each specific package format. +fn before_each_package>( + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + // The `CARGO_PACKAGER_FORMAT` environment variable is required. + let format = std::env::var("CARGO_PACKAGER_FORMAT") + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + + let package_format = format.as_str(); + println!("Running before-each-package-command for {package_format:?}"); + match package_format { + "app" | "dmg" => before_each_package_macos( package_format, host_os, &main_binary_name, &path_to_binary), + "deb" => before_each_package_deb( package_format, host_os, &main_binary_name, &path_to_binary), + "appimage" => before_each_package_appimage(package_format, host_os, &main_binary_name, &path_to_binary), + "pacman" => before_each_package_pacman( package_format, host_os, &main_binary_name, &path_to_binary), + "nsis" => before_each_package_windows( package_format, host_os, &main_binary_name, &path_to_binary), + _other => return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Unknown/unsupported package format {_other:?}"), + )), + } +} + + +/// Runs the macOS-specific build commands for "app" and "dmg" package formats. +/// +/// This function effectively runs the following shell commands: +/// ```sh +/// MAKEPAD_PACKAGE_DIR=../Resources cargo build --workspace --release \ +/// && install_name_tool -add_rpath "@executable_path/../Frameworks" ./target/release/_moly_app; +/// ``` +fn before_each_package_macos>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "macos", "'app' and 'dmg' packages can only be created on macOS."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + [("MAKEPAD", "apple_bundle")], + )?; + + // Use `install_name_tool` to add the `@executable_path` rpath to the binary. + let install_name_tool_cmd = Command::new("install_name_tool") + .arg("-add_rpath") + .arg("@executable_path/../Frameworks") + .arg(path_to_binary.as_ref()) + .spawn()?; + + let output = install_name_tool_cmd.wait_with_output()?; + if !output.status.success() { + eprintln!("Failed to run install_name_tool command: {} + ------------------------- stderr: ------------------------- + {:?}", + output.status, + String::from_utf8_lossy(&output.stderr), + ); + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Failed to run install_name_tool command for macOS")); + } + + Ok(()) + +} + +/// Runs the Linux-specific build commands for AppImage packages. +fn before_each_package_appimage>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "linux", "AppImage packages can only be created on Linux."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + strip_unneeded_linux_binaries(host_os, path_to_binary)?; + + Ok(()) +} + + +/// Runs the Linux-specific build commands for Debian `.deb` packages. +/// +/// This function effectively runs the following shell commands: +/// ```sh +/// for path in $(ldd target/release/_moly_app | awk '{print $3}'); do \ +/// basename "$/path" ; \ +/// done \ +/// | xargs dpkg -S 2> /dev/null | awk '{print $1}' | awk -F ':' '{print $1}' | sort | uniq > ./dist/depends_deb.txt; \ +/// echo "curl" >> ./dist/depends_deb.txt; \ +/// +fn before_each_package_deb>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "linux", "'deb' packages can only be created on Linux."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + + // Create Debian dependencies file by running `ldd` on the binary + // and then running `dpkg -S` on each unique shared libraries outputted by `ldd`. + let ldd_output = Command::new("ldd") + .arg(path_to_binary.as_ref()) + .output()?; + + let ldd_output = if ldd_output.status.success() { + String::from_utf8_lossy(&ldd_output.stdout) + } else { + eprintln!("Failed to run ldd command: {} + ------------------------- stderr: ------------------------- + {:?}", + ldd_output.status, + String::from_utf8_lossy(&ldd_output.stderr), + ); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to run ldd command on {host_os} for package format {package_format:?}") + )); + }; + + let mut dpkgs = Vec::new(); + for line_raw in ldd_output.lines() { + let line = line_raw.trim(); + let lib_name_opt = line.split_whitespace() + .next() + .and_then(|path| Path::new(path) + .file_name() + .and_then(|f| f.to_str().to_owned()) + ); + let Some(lib_name) = lib_name_opt else { continue }; + + let dpkg_output = Command::new("dpkg") + .arg("-S") + .arg(lib_name) + .stderr(Stdio::null()) + .output()?; + let dpkg_output = if dpkg_output.status.success() { + String::from_utf8_lossy(&dpkg_output.stdout) + } else { + // Skip shared libraries that dpkg doesn't know about, e.g., `linux-vdso.so*` + continue; + }; + + let Some(package_name) = dpkg_output.split(':').next() else { continue }; + println!("Got dpkg dependency {package_name:?} from ldd output: {line:?}"); + dpkgs.push(package_name.to_string()); + } + dpkgs.sort(); + dpkgs.dedup(); + println!("Sorted and de-duplicated dependencies: {:#?}", dpkgs); + std::fs::write("./dist/depends_deb.txt", dpkgs.join("\n"))?; + + strip_unneeded_linux_binaries(host_os, path_to_binary)?; + Ok(()) +} + + +/// Runs the Linux-specific build commands for PacMan packages. +/// +/// This is untested and may be incomplete, e.g., dependencies are not determined. +fn before_each_package_pacman>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "linux", "Pacman packages can only be created on Linux."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + strip_unneeded_linux_binaries(host_os, path_to_binary)?; + Ok(()) +} + +/// Runs the Windows-specific build commands for WiX (`.msi`) and NSIS (`.exe`) packages. +fn before_each_package_windows>( + package_format: &str, + host_os: &str, + main_binary_name: &str, + _path_to_binary: P, +) -> std::io::Result<()> { + assert!(host_os == "windows", "'.exe' and '.msi' packages can only be created on Windows."); + + cargo_build( + package_format, + host_os, + main_binary_name, + EMPTY_ARGS, + EMPTY_ENVS, + )?; + + Ok(()) +} + +fn cargo_build( + package_format: &str, + _host_os: &str, + main_binary_name: &str, + extra_args: I, + extra_env: E, +) -> std::io::Result<()> +where + I: IntoIterator, + A: AsRef, + E: IntoIterator, + K: AsRef, + V: AsRef, +{ + let cargo_build_cmd = Command::new("cargo") + .arg("build") + .arg("--workspace") + .arg("--release") + .args(extra_args) + .env("MAKEPAD_PACKAGE_DIR", &makepad_package_dir_value(package_format, main_binary_name)) + .envs(extra_env) + .spawn()?; + + let output = cargo_build_cmd.wait_with_output()?; + if !output.status.success() { + eprintln!("Failed to run cargo build command: {} + ------------------------- stderr: ------------------------- + {:?}", + output.status, + String::from_utf8_lossy(&output.stderr), + ); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to run cargo build command on {_host_os} for package format {package_format:?}") + )); + } + + Ok(()) +} +/// Strips unneeded symbols from the Linux binary, which is required for Debian `.deb` packages +/// and recommended for all other Linux package formats. +fn strip_unneeded_linux_binaries>(host_os: &str, path_to_binary: P) -> std::io::Result<()> { + assert!(host_os == "linux", "'strip --strip-unneeded' can only be run on Linux."); + let strip_cmd = Command::new("strip") + .arg("--strip-unneeded") + .arg("--remove-section=.comment") + .arg("--remove-section=.note") + .arg(path_to_binary.as_ref()) + .spawn()?; + + let output = strip_cmd.wait_with_output()?; + if !output.status.success() { + eprintln!("Failed to run strip command: {} + ------------------------- stderr: ------------------------- + {:?}", + output.status, + String::from_utf8_lossy(&output.stderr), + ); + return Err(std::io::Error::new(std::io::ErrorKind::Other, "Failed to run strip command for Linux")); + } + + Ok(()) +} diff --git a/packaging/robrix.desktop b/packaging/robrix.desktop new file mode 100644 index 00000000..7da8abba --- /dev/null +++ b/packaging/robrix.desktop @@ -0,0 +1,14 @@ +[Desktop Entry] +Categories={{categories}} +{{#if comment}} +Comment={{comment}} +{{/if}} +Exec={{exec}} {{exec_arg}} +Icon={{icon}} +Name={{name}} +Terminal=false +Type=Application +StartupWMClass=robrix +{{#if mime_type}} +MimeType={{mime_type}} +{{/if}} diff --git a/packaging/robrix_logo_alpha.png b/packaging/robrix_logo_alpha.png new file mode 100644 index 00000000..e0ba3a65 Binary files /dev/null and b/packaging/robrix_logo_alpha.png differ diff --git a/resources/icons/location-person.svg b/resources/icons/location-person.svg new file mode 100644 index 00000000..981447a4 --- /dev/null +++ b/resources/icons/location-person.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index 04fae7b9..d062ea93 100644 --- a/src/app.rs +++ b/src/app.rs @@ -2,7 +2,7 @@ use makepad_widgets::*; use matrix_sdk::ruma::OwnedRoomId; use crate::{ - home::rooms_list::RoomListAction, + home::{main_desktop_ui::RoomsPanelAction, rooms_list::RoomListAction}, verification::VerificationAction, verification_modal::{VerificationModalAction, VerificationModalWidgetRefExt}, }; @@ -13,9 +13,7 @@ live_design! { import makepad_draw::shader::std::*; import crate::shared::styles::*; - import crate::shared::clickable_view::ClickableView; import crate::home::home_screen::HomeScreen; - import crate::home::room_screen::RoomScreen; import crate::profile::my_profile_screen::MyProfileScreen; import crate::verification_modal::VerificationModal; @@ -189,6 +187,19 @@ impl MatchEvent for App { RoomListAction::None => { } } + match action.as_widget_action().cast() { + RoomsPanelAction::RoomFocused(room_id) => { + self.app_state.rooms_panel.selected_room = Some(SelectedRoom { + id: room_id.clone(), + name: None + }); + } + RoomsPanelAction::FocusNone => { + self.app_state.rooms_panel.selected_room = None; + } + _ => { } + } + // `VerificationAction`s come from a background thread, so they are NOT widget actions. // Therefore, we cannot use `as_widget_action().cast()` to match them. match action.downcast_ref() { diff --git a/src/home/home_screen.rs b/src/home/home_screen.rs index e72c3ad3..dc935da8 100644 --- a/src/home/home_screen.rs +++ b/src/home/home_screen.rs @@ -5,11 +5,13 @@ live_design! { import makepad_widgets::theme_desktop_dark::*; import makepad_draw::shader::std::*; - import crate::home::main_content::MainContent; + import crate::home::main_mobile_ui::MainMobileUI; import crate::home::rooms_sidebar::RoomsSideBar; import crate::home::spaces_dock::SpacesDock; import crate::shared::styles::*; import crate::shared::adaptive_view::AdaptiveView; + import crate::shared::search_bar::SearchBar; + import crate::home::main_desktop_ui::MainDesktopUI; NavigationWrapper = {{NavigationWrapper}} { view_stack = {} @@ -24,10 +26,15 @@ live_design! { width: Fill, height: Fill padding: 0, margin: 0, align: {x: 0.0, y: 0.0} flow: Right - + spaces = {} - rooms_sidebar = {} - main_content = {} + + { + flow: Down + width: Fill, height: Fill + {} + {} + } } Mobile = { @@ -47,11 +54,11 @@ live_design! { sidebar = {} spaces = {} } - + main_content_view = { width: Fill, height: Fill body = { - main_content = {} + main_content = {} } } } @@ -63,7 +70,7 @@ live_design! { #[derive(Live, LiveHook, Widget)] pub struct NavigationWrapper { #[deref] - view: View + view: View, } impl Widget for NavigationWrapper { @@ -77,7 +84,8 @@ impl Widget for NavigationWrapper { } impl MatchEvent for NavigationWrapper { - fn handle_actions(&mut self, cx: &mut Cx, actions:&Actions) { - self.stack_navigation(id!(view_stack)).handle_stack_view_actions(cx, actions); + fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) { + self.stack_navigation(id!(view_stack)) + .handle_stack_view_actions(cx, actions); } } diff --git a/src/home/light_themed_dock.rs b/src/home/light_themed_dock.rs new file mode 100644 index 00000000..2dd60a49 --- /dev/null +++ b/src/home/light_themed_dock.rs @@ -0,0 +1,298 @@ +use makepad_widgets::*; + +live_design! { + import makepad_widgets::base::*; + import makepad_widgets::theme_desktop_dark::*; + import makepad_draw::shader::std::*; + + import crate::shared::styles::*; + + Splitter = { + draw_splitter: { + uniform border_radius: 1.0 + uniform splitter_pad: 1.0 + uniform splitter_grabber: 110.0 + + instance pressed: 0.0 + instance hover: 0.0 + + fn pixel(self) -> vec4 { + let sdf = Sdf2d::viewport(self.pos * self.rect_size); + sdf.clear(#ef); + + if self.is_vertical > 0.5 { + sdf.box( + self.splitter_pad, + self.rect_size.y * 0.5 - self.splitter_grabber * 0.5, + self.rect_size.x - 2.0 * self.splitter_pad, + self.splitter_grabber, + self.border_radius + ); + } + else { + sdf.box( + self.rect_size.x * 0.5 - self.splitter_grabber * 0.5, + self.splitter_pad, + self.splitter_grabber, + self.rect_size.y - 2.0 * self.splitter_pad, + self.border_radius + ); + } + return sdf.fill_keep(mix( + THEME_COLOR_D_HIDDEN, + mix( + THEME_COLOR_CTRL_SCROLLBAR_HOVER, + THEME_COLOR_CTRL_SCROLLBAR_HOVER * 1.2, + self.pressed + ), + self.hover + )); + } + } + split_bar_size: (THEME_SPLITTER_SIZE) + min_horizontal: (THEME_SPLITTER_MIN_HORIZONTAL) + max_horizontal: (THEME_SPLITTER_MAX_HORIZONTAL) + min_vertical: (THEME_SPLITTER_MIN_VERTICAL) + max_vertical: (THEME_SPLITTER_MAX_VERTICAL) + + animator: { + hover = { + default: off + off = { + from: {all: Forward {duration: 0.1}} + apply: { + draw_splitter: {pressed: 0.0, hover: 0.0} + } + } + + on = { + from: { + all: Forward {duration: 0.1} + state_down: Forward {duration: 0.01} + } + apply: { + draw_splitter: { + pressed: 0.0, + hover: [{time: 0.0, value: 1.0}], + } + } + } + + pressed = { + from: { all: Forward { duration: 0.1 }} + apply: { + draw_splitter: { + pressed: [{time: 0.0, value: 1.0}], + hover: 1.0, + } + } + } + } + } + } + + TabCloseButton = { + // TODO: NEEDS FOCUS STATE + height: 10.0, width: 10.0, + margin: { right: (THEME_SPACE_2), left: -3.5 }, + draw_button: { + + instance hover: float; + instance selected: float; + + fn pixel(self) -> vec4 { + let sdf = Sdf2d::viewport(self.pos * self.rect_size); + let mid = self.rect_size / 2.0; + let size = (self.hover * 0.25 + 0.5) * 0.25 * length(self.rect_size); + let min = mid - vec2(size); + let max = mid + vec2(size); + sdf.move_to(min.x, min.y); + sdf.line_to(max.x, max.y); + sdf.move_to(min.x, max.y); + sdf.line_to(max.x, min.y); + return sdf.stroke(mix( + #f, + #4, + self.hover + ), 1.0); + } + } + + animator: { + hover = { + default: off + off = { + from: {all: Forward {duration: 0.1}} + apply: { + draw_button: {hover: 0.0} + } + } + + on = { + cursor: Hand, + from: {all: Snap} + apply: { + draw_button: {hover: 1.0} + } + } + } + } + } + + Tab = { + width: Fit, height: Fill, //Fixed((THEME_TAB_HEIGHT)), + + align: {x: 0.0, y: 0.5} + padding: { } + + close_button: {} + draw_name: { + text_style: {} + instance hover: 0.0 + instance selected: 0.0 + fn get_color(self) -> vec4 { + return mix( + mix( + #x0, // THEME_COLOR_TEXT_INACTIVE, + #xf, // THEME_COLOR_TEXT_SELECTED, + self.selected + ), + THEME_COLOR_TEXT_HOVER, + self.hover + ) + } + } + + draw_bg: { + instance hover: float + instance selected: float + + fn pixel(self) -> vec4 { + let sdf = Sdf2d::viewport(self.pos * self.rect_size); + sdf.box( + -1., + -1., + self.rect_size.x + 2, + self.rect_size.y + 2, + 1. + ) + sdf.fill_keep( + mix( + (COLOR_SECONDARY) * 0.95, + (COLOR_SELECTED_PRIMARY), + self.selected + ) + ) + return sdf.result + } + } + + animator: { + hover = { + default: off + off = { + from: {all: Forward {duration: 0.2}} + apply: { + draw_bg: {hover: 0.0} + draw_name: {hover: 0.0} + } + } + + on = { + cursor: Hand, + from: {all: Forward {duration: 0.1}} + apply: { + draw_bg: {hover: [{time: 0.0, value: 1.0}]} + draw_name: {hover: [{time: 0.0, value: 1.0}]} + } + } + } + + selected = { + default: off + off = { + from: {all: Forward {duration: 0.3}} + apply: { + close_button: {draw_button: {selected: 0.0}} + draw_bg: {selected: 0.0} + draw_name: {selected: 0.0} + } + } + + on = { + from: {all: Snap} + apply: { + close_button: {draw_button: {selected: 1.0}} + draw_bg: {selected: 1.0} + draw_name: {selected: 1.0} + } + } + } + } + } + + TabBar = { + CloseableTab = {closeable:true} + PermanentTab = {closeable:false} + + draw_drag: { + draw_depth: 10 + color: #x0 + } + draw_fill: { + color: (COLOR_SECONDARY) + } + + width: Fill, height: (THEME_TAB_HEIGHT) + + scroll_bars: { + show_scroll_x: true + show_scroll_y: false + scroll_bar_x: { + draw_bar: {bar_width: 3.0} + bar_size: 4 + use_vertical_finger_scroll: true + } + } + } + + Dock = { + flow: Down, + + round_corner: { + draw_depth: 20.0 + border_radius: 20. + fn pixel(self) -> vec4 { + let pos = vec2( + mix(self.pos.x, 1.0 - self.pos.x, self.flip.x), + mix(self.pos.y, 1.0 - self.pos.y, self.flip.y) + ) + + let sdf = Sdf2d::viewport(pos * self.rect_size); + sdf.rect(-10., -10., self.rect_size.x * 2.0, self.rect_size.y * 2.0); + sdf.box( + 0.25, + 0.25, + self.rect_size.x * 2.0, + self.rect_size.y * 2.0, + 4.0 + ); + + sdf.subtract() + // sdf.fill(THEME_COLOR_BG_APP) + sdf.fill(COLOR_SECONDARY) + return sdf.result + } + } + border_size: (THEME_DOCK_BORDER_SIZE) + + padding: {left: (THEME_DOCK_BORDER_SIZE), top: 0, right: (THEME_DOCK_BORDER_SIZE), bottom: (THEME_DOCK_BORDER_SIZE)} + padding_fill: {color: (THEME_COLOR_BG_APP)} // TODO: unclear what this does + drag_quad: { + draw_depth: 10.0 + color: (mix((COLOR_SECONDARY), #FFFFFF00, pow(0.25, THEME_COLOR_CONTRAST))) + } + tab_bar: {} + splitter: {} + } +} \ No newline at end of file diff --git a/src/home/main_desktop_ui.rs b/src/home/main_desktop_ui.rs new file mode 100644 index 00000000..bae31bf0 --- /dev/null +++ b/src/home/main_desktop_ui.rs @@ -0,0 +1,260 @@ +use makepad_widgets::*; +use matrix_sdk::ruma::OwnedRoomId; +use std::collections::HashMap; + +use crate::app::{AppState, SelectedRoom}; + +use super::room_screen::RoomScreenWidgetRefExt; +live_design! { + import makepad_widgets::base::*; + import makepad_widgets::theme_desktop_dark::*; + import makepad_draw::shader::std::*; + + import crate::shared::styles::*; + import crate::home::light_themed_dock::*; + import crate::home::welcome_screen::WelcomeScreen; + import crate::home::rooms_sidebar::RoomsSideBar; + import crate::home::room_screen::RoomScreen; + + MainDesktopUI = {{MainDesktopUI}} { + dock = { + width: Fill, + height: Fill, + padding: 0, + spacing: 0, + + root = Splitter { + axis: Horizontal, + align: FromA(300.0), + a: rooms_sidebar_tab, + b: main + } + + // Not really a tab, but it needs to be one to be used in the dock + rooms_sidebar_tab = Tab { + name: "" // show no tab header + kind: rooms_sidebar + } + + main = Tabs{tabs:[home_tab], selected:0} + + home_tab = Tab { + name: "Home" + kind: welcome_screen + template: PermanentTab + } + + rooms_sidebar = {} + welcome_screen = {} + room_screen = {} + } + } +} + +#[derive(Live, LiveHook, Widget)] +pub struct MainDesktopUI { + #[deref] + view: View, + + /// The rooms that are currently open + #[rust] + open_rooms: HashMap, + + /// The tab that should be closed in the next draw event + #[rust] + tab_to_close: Option, + + /// The order in which the rooms were opened + #[rust] + room_order: Vec, +} + +impl Widget for MainDesktopUI { + fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) { + self.match_event(cx, event); + self.view.handle_event(cx, event, scope); + } + + fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep { + let dock = self.view.dock(id!(dock)); + let app_state = scope.data.get_mut::().unwrap(); + + // In case a room is closed, this will be the newly selected room if there is one + let mut new_selected_room: Option = None; + let mut focus_reset = false; + + if let Some(tab_id) = self.tab_to_close { + if let Some(room_id) = self.open_rooms.get(&tab_id) { + self.room_order.remove( + self.room_order.iter().position(|id| id == room_id).unwrap() + ); + + if self.open_rooms.len() > 1 { + // if the closing tab is the active one, then focus the next room + let active_room = app_state.rooms_panel.selected_room.as_ref(); + if let Some(active_room) = active_room { + if active_room.id == *room_id { + if let Some(new_focused_room_id) = self.room_order.last() { + // notify the app state about the new focused room + cx.widget_action( + self.widget_uid(), + &scope.path, + RoomsPanelAction::RoomFocused(new_focused_room_id.clone()), + ); + + // set the new selected room to be used in the current draw + new_selected_room = Some(SelectedRoom { + id: new_focused_room_id.clone(), + name: None + }); + } + } + } + } else { + // if there is no room to focus, reset the selected room in the app state + // app_state.rooms_panel.selected_room = None; + cx.widget_action( + self.widget_uid(), + &scope.path, + RoomsPanelAction::FocusNone, + ); + + focus_reset = true; + dock.select_tab(cx, live_id!(home_tab)); + } + } + dock.close_tab(cx, tab_id); + self.tab_to_close = None; + self.open_rooms.remove(&tab_id); + } + + // if the focus was not reset, then focus the new selected room or the previously selected one + if !focus_reset { + if let Some(room) = new_selected_room { + self.focus_or_create_tab(cx, &room); + } else if let Some(room) = app_state.rooms_panel.selected_room.as_ref() { + self.focus_or_create_tab(cx, room); + } + } + self.view.draw_walk(cx, scope, walk) + } +} + +impl MainDesktopUI { + /// Focuses on a room if it is already open, otherwise creates a new tab for the room + fn focus_or_create_tab(&mut self, cx: &mut Cx2d, room: &SelectedRoom) { + let dock = self.view.dock(id!(dock)); + + // if the room is already open, select its tab + let room_id_as_live_id = LiveId::from_str(&room.id.to_string()); + if self.open_rooms.contains_key(&room_id_as_live_id) { + dock.select_tab(cx, room_id_as_live_id); + return; + } + + self.open_rooms.insert(room_id_as_live_id, room.id.clone()); + + let displayed_room_name = room + .name + .clone() + .unwrap_or_else(|| format!("Room ID {}", &room.id)); + + // create a new tab for the room + let (tab_bar, _pos) = dock.find_tab_bar_of_tab(live_id!(home_tab)).unwrap(); + let kind = live_id!(room_screen); + + let result = dock.create_and_select_tab( + cx, + tab_bar, + room_id_as_live_id, + kind, + displayed_room_name.clone(), + live_id!(CloseableTab), + // `None` will insert the tab at the end + None, + ); + + // if the tab was created, set the room screen and add the room to the room order + if let Some(widget) = result { + self.room_order.push(room.id.clone()); + widget.as_room_screen().set_displayed_room( + cx, + displayed_room_name, + room.id.clone(), + ); + } + } +} + +impl MatchEvent for MainDesktopUI { + fn handle_action(&mut self, cx: &mut Cx, action: &Action) { + let dock = self.view.dock(id!(dock)); + + if let Some(action) = action.as_widget_action() { + match action.cast() { + // Whenever a tab is pressed notify the app state about it, except for the home tab + DockAction::TabWasPressed(tab_id) => { + if tab_id == live_id!(home_tab) { + cx.widget_action( + self.widget_uid(), + &HeapLiveIdPath::default(), + RoomsPanelAction::FocusNone, + ); + } else if let Some(room_id) = self.open_rooms.get(&tab_id) { + cx.widget_action( + self.widget_uid(), + &HeapLiveIdPath::default(), + RoomsPanelAction::RoomFocused(room_id.clone()), + ); + } + } + // Whenever a tab is closed, defer the close to the next event loop to prevent closing the tab while the app state + // still has the room as selected + DockAction::TabCloseWasPressed(tab_id) => { + self.tab_to_close = Some(tab_id); + self.redraw(cx); + } + // When dragging a tab, allow it to be dragged + DockAction::ShouldTabStartDrag(tab_id) => { + dock.tab_start_drag( + cx, + tab_id, + DragItem::FilePath { + path: "".to_string(), + internal_id: Some(tab_id), + }, + ); + } + // When dragging a tab, allow it to be dragged + DockAction::Drag(drag_event) => { + if drag_event.items.len() == 1 { + dock.accept_drag(cx, drag_event, DragResponse::Move); + } + } + // When dropping a tab, move it to the new position + DockAction::Drop(drop_event) => { + if let DragItem::FilePath { + path: _, + internal_id, + } = &drop_event.items[0] + { + // from inside the dock, otherwise it's an external file + if let Some(internal_id) = internal_id { + dock.drop_move(cx, drop_event.abs, *internal_id); + } + } + } + _ => (), + } + } + } +} + +#[derive(Clone, DefaultNone, Debug)] +pub enum RoomsPanelAction { + None, + /// Notifies that a room was focused + RoomFocused(OwnedRoomId), + /// Resets the focus on the rooms panel + FocusNone, +} diff --git a/src/home/main_content.rs b/src/home/main_mobile_ui.rs similarity index 86% rename from src/home/main_content.rs rename to src/home/main_mobile_ui.rs index faf7356d..d515dc63 100644 --- a/src/home/main_content.rs +++ b/src/home/main_mobile_ui.rs @@ -10,13 +10,12 @@ live_design! { import makepad_draw::shader::std::*; import crate::shared::styles::*; - import crate::shared::search_bar::SearchBar; import crate::shared::cached_widget::CachedWidget; import crate::home::room_screen::RoomScreen; import crate::home::welcome_screen::WelcomeScreen; - MainContent = {{MainContent}} { + MainMobileUI = {{MainMobileUI}} { width: Fill, height: Fill flow: Down, show_bg: true @@ -25,32 +24,28 @@ live_design! { } align: {x: 0.0, y: 0.5} - {} welcome = {} rooms = { align: {x: 0.5, y: 0.5} width: Fill, height: Fill - { - room_screen = {} - } + room_screen = {} } } } #[derive(Live, LiveHook, Widget)] -pub struct MainContent { +pub struct MainMobileUI { #[deref] view: View, } -impl Widget for MainContent { +impl Widget for MainMobileUI { fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) { self.view.handle_event(cx, event, scope); } fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep { - // log!("Redrawing MainContent"); let app_state = scope.data.get::().unwrap(); if let Some(room) = app_state.rooms_panel.selected_room.as_ref() { diff --git a/src/home/mod.rs b/src/home/mod.rs index f4e0a250..6e03136d 100644 --- a/src/home/mod.rs +++ b/src/home/mod.rs @@ -1,13 +1,15 @@ use makepad_widgets::Cx; pub mod home_screen; -pub mod main_content; +pub mod main_mobile_ui; +pub mod main_desktop_ui; pub mod room_preview; pub mod room_screen; pub mod rooms_list; pub mod rooms_sidebar; pub mod spaces_dock; pub mod welcome_screen; +pub mod light_themed_dock; pub fn live_design(cx: &mut Cx) { home_screen::live_design(cx); @@ -15,7 +17,9 @@ pub fn live_design(cx: &mut Cx) { room_preview::live_design(cx); room_screen::live_design(cx); rooms_sidebar::live_design(cx); - main_content::live_design(cx); + main_mobile_ui::live_design(cx); + main_desktop_ui::live_design(cx); spaces_dock::live_design(cx); welcome_screen::live_design(cx); + light_themed_dock::live_design(cx); } diff --git a/src/home/room_preview.rs b/src/home/room_preview.rs index cb559e39..4d0f6d2a 100644 --- a/src/home/room_preview.rs +++ b/src/home/room_preview.rs @@ -2,7 +2,8 @@ use makepad_widgets::*; use crate::{ shared::{ - adaptive_view::{AdaptiveViewWidgetExt, DisplayContext}, avatar::AvatarWidgetExt, + adaptive_view::{AdaptiveViewWidgetExt, DisplayContext}, + avatar::AvatarWidgetExt, html_or_plaintext::HtmlOrPlaintextWidgetExt, }, utils::{self, relative_format}, @@ -101,7 +102,7 @@ live_design! { } } } - + RoomPreview = {{RoomPreview}} { // Wraps the RoomPreviewContent in an AdaptiveView // to change the displayed content (and its layout) based on the available space in the sidebar. @@ -155,12 +156,10 @@ impl LiveHook for RoomPreview { // Adapt the preview based on the available space. self.view .adaptive_view(id!(adaptive_preview)) - .set_variant_selector(cx, |_cx, parent_size| { - match parent_size.x { - x if x <= 100. => live_id!(OnlyIcon), - x if x <= 250. => live_id!(IconAndName), - _ => live_id!(FullPreview), - } + .set_variant_selector(cx, |_cx, parent_size| match parent_size.x { + x if x <= 100. => live_id!(OnlyIcon), + x if x <= 250. => live_id!(IconAndName), + _ => live_id!(FullPreview), }); } } @@ -242,7 +241,7 @@ impl Widget for RoomPreviewContent { // Mobile doesn't have a selected state. Always use the default colors. // We call the update in case the app was resized from desktop to mobile while the room was selected. // This can be optimized by only calling this when the app is resized. - self.update_preview_colors(cx, false); + self.update_preview_colors(cx, false); } } self.view.draw_walk(cx, scope, walk) @@ -279,44 +278,51 @@ impl RoomPreviewContent { ), ); - self.view.label(id!(room_name)).apply_over( - cx, - live!( + // We check that the UI elements exist to avoid unnecessary updates, and prevent error logs. + if !self.view.label(id!(room_name)).is_empty() { + self.view.label(id!(room_name)).apply_over( + cx, + live!( draw_text: { color: (room_name_color) } - ), - ); + ), + ); + } - self.view.label(id!(timestamp)).apply_over( - cx, - live!( - draw_text: { - color: (timestamp_color) - } - ), - ); + if !self.view.label(id!(timestamp)).is_empty() { + self.view.label(id!(timestamp)).apply_over( + cx, + live!( + draw_text: { + color: (timestamp_color) + } + ), + ); + } - self.html_or_plaintext(id!(latest_message)).apply_over( - cx, - live!( - html_view = { - html = { - font_color: (message_text_color), - draw_normal: { color: (message_text_color) }, - draw_italic: { color: (message_text_color) }, - draw_bold: { color: (message_text_color) }, - draw_bold_italic: { color: (message_text_color) }, - } + if !self.view.html_or_plaintext(id!(latest_message)).is_empty() { + self.view.html_or_plaintext(id!(latest_message)).apply_over( + cx, + live!( + html_view = { + html = { + font_color: (message_text_color), + draw_normal: { color: (message_text_color) }, + draw_italic: { color: (message_text_color) }, + draw_bold: { color: (message_text_color) }, + draw_bold_italic: { color: (message_text_color) }, } - plaintext_view = { - pt_label = { - draw_text: { - color: (message_text_color) - } + } + plaintext_view = { + pt_label = { + draw_text: { + color: (message_text_color) } } - ), - ); + } + ), + ); + } } } diff --git a/src/home/room_screen.rs b/src/home/room_screen.rs index 0f8ad193..904f86c9 100644 --- a/src/home/room_screen.rs +++ b/src/home/room_screen.rs @@ -1,7 +1,7 @@ //! A room screen is the UI page that displays a single Room's timeline of events/messages //! along with a message input bar at the bottom. -use std::{borrow::Cow, collections::{BTreeMap, HashMap}, ops::{DerefMut, Range}, sync::{Arc, Mutex}, time::Instant}; +use std::{borrow::Cow, collections::{BTreeMap, HashMap}, ops::{DerefMut, Range}, sync::{Arc, Mutex}, time::{Instant, SystemTime}}; use imbl::Vector; use makepad_widgets::*; @@ -9,8 +9,7 @@ use matrix_sdk::{ ruma::{ events::room::{ message::{ - ImageMessageEventContent, MessageFormat, MessageType, RoomMessageEventContent, - TextMessageEventContent, + FormattedBody, ImageMessageEventContent, LocationMessageEventContent, MessageFormat, MessageType, NoticeMessageEventContent, RoomMessageEventContent, TextMessageEventContent }, MediaSource, }, @@ -23,9 +22,10 @@ use matrix_sdk_ui::timeline::{ RoomMembershipChange, TimelineDetails, TimelineItem, TimelineItemContent, TimelineItemKind, VirtualTimelineItem, }; +use robius_location::Coordinates; use crate::{ - avatar_cache::{self, AvatarCacheEntry}, event_preview::{text_preview_of_member_profile_change, text_preview_of_other_state, text_preview_of_redacted_message, text_preview_of_room_membership_change, text_preview_of_timeline_item}, media_cache::{MediaCache, MediaCacheEntry}, profile::{ + avatar_cache::{self, AvatarCacheEntry}, event_preview::{text_preview_of_member_profile_change, text_preview_of_other_state, text_preview_of_redacted_message, text_preview_of_room_membership_change, text_preview_of_timeline_item}, location::{get_latest_location, init_location_subscriber, request_location_update, LocationAction, LocationRequest, LocationUpdate}, media_cache::{MediaCache, MediaCacheEntry}, profile::{ user_profile::{AvatarState, ShowUserProfileAction, UserProfile, UserProfileAndRoomId, UserProfilePaneInfo, UserProfileSlidingPaneRef, UserProfileSlidingPaneWidgetExt}, user_profile_cache, }, shared::{ @@ -33,11 +33,12 @@ use crate::{ html_or_plaintext::{HtmlOrPlaintextRef, HtmlOrPlaintextWidgetRefExt}, text_or_image::{TextOrImageRef, TextOrImageWidgetRefExt}, typing_animation::TypingAnimationWidgetExt, - }, - sliding_sync::{get_client, submit_async_request, take_timeline_update_receiver, MatrixRequest}, utils::{self, unix_time_millis_to_datetime, MediaFormatConst} + }, sliding_sync::{get_client, submit_async_request, take_timeline_update_receiver, MatrixRequest}, utils::{self, unix_time_millis_to_datetime, MediaFormatConst} }; use rangemap::RangeSet; +const GEO_URI_SCHEME: &str = "geo:"; + live_design! { import makepad_draw::shader::std::*; import makepad_widgets::base::*; @@ -51,6 +52,7 @@ live_design! { import crate::shared::html_or_plaintext::*; import crate::profile::user_profile::UserProfileSlidingPane; import crate::shared::typing_animation::TypingAnimation; + import crate::shared::icon_button::RobrixIconButton; IMG_DEFAULT_AVATAR = dep("crate://self/resources/img/default_avatar.png") ICO_FAV = dep("crate://self/resources/icon_favorite.svg") @@ -62,6 +64,8 @@ live_design! { ICO_ADD = dep("crate://self/resources/icon_add.svg") ICO_CLOSE = dep("crate://self/resources/icons/close.svg") ICO_JUMP_TO_BOTTOM = dep("crate://self/resources/icon_jump_to_bottom.svg") + + ICO_LOCATION_PERSON = dep("crate://self/resources/icons/location-person.svg") TEXT_SUB = { font_size: (10), @@ -679,39 +683,100 @@ live_design! { } - IMG_SMILEY_FACE_BW = dep("crate://self/resources/img/smiley_face_bw.png") - IMG_PLUS = dep("crate://self/resources/img/plus.png") - IMG_KEYBOARD_ICON = dep("crate://self/resources/img/keyboard_icon.png") + LocationPreview = {{LocationPreview}} { + visible: false + width: Fill + height: Fit + flow: Down + padding: {left: 12.0, top: 12.0, bottom: 12.0, right: 10.0} + spacing: 15 - RoomScreen = {{RoomScreen}} { - width: Fill, height: Fill, show_bg: true, draw_bg: { - color: (COLOR_SECONDARY) + color: #xF0F5FF, } - flow: Down, spacing: 0.0 +