mirror of
https://github.com/HuLaSpark/HuLa.git
synced 2026-09-24 23:11:59 +08:00
fix(ios): 🐛 fix ios keyboard pop-up problem
This commit is contained in:
Generated
+127
-79
@@ -201,11 +201,11 @@ dependencies = [
|
||||
"clipboard-win",
|
||||
"image 0.25.6",
|
||||
"log",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -698,11 +698,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2"
|
||||
checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5"
|
||||
dependencies = [
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1600,7 +1600,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2486,7 +2486,7 @@ checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"keyboard-types",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"once_cell",
|
||||
"serde",
|
||||
@@ -2804,6 +2804,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"async-walkdir",
|
||||
"base64 0.22.1",
|
||||
"block2 0.6.2",
|
||||
"cc",
|
||||
"chrono",
|
||||
"config",
|
||||
@@ -2821,8 +2822,11 @@ dependencies = [
|
||||
"mime_guess",
|
||||
"moka",
|
||||
"ndk-context",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-ui-kit",
|
||||
"pulldown-cmark",
|
||||
"reqwest 0.11.27",
|
||||
"rodio",
|
||||
@@ -3662,8 +3666,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "119c8490084af61b44c9eda9d626475847a186737c0378c85e32d77c33a01cd4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
"time",
|
||||
]
|
||||
|
||||
@@ -3859,10 +3863,10 @@ dependencies = [
|
||||
"dpi",
|
||||
"gtk",
|
||||
"keyboard-types",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
@@ -4154,9 +4158,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc"
|
||||
checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
"objc2-exception-helper",
|
||||
@@ -4185,27 +4189,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"block2 0.6.1",
|
||||
"block2 0.6.2",
|
||||
"libc",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data 0.3.1",
|
||||
"objc2-core-data 0.3.2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-core-image 0.3.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-quartz-core 0.3.1",
|
||||
"objc2-core-image 0.3.2",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-quartz-core 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d"
|
||||
checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4222,35 +4226,37 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-data"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d"
|
||||
checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
|
||||
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"block2 0.6.2",
|
||||
"dispatch2",
|
||||
"objc2 0.6.2",
|
||||
"libc",
|
||||
"objc2 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4"
|
||||
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"dispatch2",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
@@ -4269,12 +4275,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-image"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e"
|
||||
checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006"
|
||||
dependencies = [
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-location"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
|
||||
dependencies = [
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-text"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4307,25 +4335,25 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
|
||||
checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"block2 0.6.1",
|
||||
"block2 0.6.2",
|
||||
"libc",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c"
|
||||
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
@@ -4335,7 +4363,7 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9052cb1bb50a4c161d934befcf879526fb87ae9a68858f241e693ca46225cf5a"
|
||||
dependencies = [
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
@@ -4358,9 +4386,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26bb88504b5a050dbba515d2414607bf5e57dd56b107bc5f0351197a3e7bdc5d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4378,13 +4406,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5"
|
||||
checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4394,20 +4423,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1f8e0ef3ab66b08c42644dcb34dba6ec0a574bbd8adbb8bdbdc7a2779731a44"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed"
|
||||
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"objc2 0.6.2",
|
||||
"block2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data 0.3.2",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-core-graphics",
|
||||
"objc2-core-image 0.3.2",
|
||||
"objc2-core-location",
|
||||
"objc2-core-text",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-quartz-core 0.3.2",
|
||||
"objc2-user-notifications",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-user-notifications"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
|
||||
dependencies = [
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4417,11 +4465,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"block2 0.6.1",
|
||||
"objc2 0.6.2",
|
||||
"block2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-javascript-core",
|
||||
"objc2-security",
|
||||
]
|
||||
@@ -4544,8 +4592,8 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
||||
dependencies = [
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -7032,7 +7080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
|
||||
dependencies = [
|
||||
"bitflags 2.9.2",
|
||||
"block2 0.6.1",
|
||||
"block2 0.6.2",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics 0.24.0",
|
||||
"crossbeam-channel",
|
||||
@@ -7049,9 +7097,9 @@ dependencies = [
|
||||
"ndk 0.9.0",
|
||||
"ndk-context",
|
||||
"ndk-sys 0.6.0+11769913",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"raw-window-handle",
|
||||
@@ -7123,9 +7171,9 @@ dependencies = [
|
||||
"log",
|
||||
"mime",
|
||||
"muda",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"percent-encoding",
|
||||
@@ -7374,8 +7422,8 @@ dependencies = [
|
||||
"byte-unit",
|
||||
"fern",
|
||||
"log",
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
@@ -7600,7 +7648,7 @@ dependencies = [
|
||||
"gtk",
|
||||
"http 1.3.1",
|
||||
"jni",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"raw-window-handle",
|
||||
@@ -7624,9 +7672,9 @@ dependencies = [
|
||||
"http 1.3.1",
|
||||
"jni",
|
||||
"log",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"raw-window-handle",
|
||||
@@ -8157,11 +8205,11 @@ dependencies = [
|
||||
"dirs 6.0.0",
|
||||
"libappindicator",
|
||||
"muda",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
@@ -8903,10 +8951,10 @@ version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c"
|
||||
dependencies = [
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"raw-window-handle",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-version",
|
||||
@@ -9638,7 +9686,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728b7d4c8ec8d81cab295e0b5b8a4c263c0d41a785fb8f8c4df284e5411140a2"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"block2 0.6.1",
|
||||
"block2 0.6.2",
|
||||
"cookie",
|
||||
"crossbeam-channel",
|
||||
"dirs 6.0.0",
|
||||
@@ -9653,10 +9701,10 @@ dependencies = [
|
||||
"kuchikiki",
|
||||
"libc",
|
||||
"ndk 0.9.0",
|
||||
"objc2 0.6.2",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit 0.3.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"once_cell",
|
||||
|
||||
@@ -109,6 +109,13 @@ tauri-plugin-safe-area-insets = "0.1.0"
|
||||
tauri-plugin-hula = { path="../tauri-plugin-hula"}
|
||||
tauri-plugin-barcode-scanner = "2.4.0"
|
||||
|
||||
[target."cfg(target_os = \"ios\")".dependencies]
|
||||
objc2 = "0.6.3"
|
||||
objc2-core-foundation = "0.3.2"
|
||||
objc2-foundation = "0.3.2"
|
||||
objc2-ui-kit = "0.3.2"
|
||||
block2 = "0.6.2"
|
||||
|
||||
[target."cfg(target_os = \"android\")".dependencies]
|
||||
jni = "0.21"
|
||||
ndk-context = "0.1"
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -131,6 +131,7 @@ pub async fn parse_markdown(app: AppHandle, file_path: String) -> Result<String,
|
||||
|
||||
/// 读取 README markdown 文件
|
||||
#[tauri::command]
|
||||
#[allow(unused_variables)]
|
||||
pub async fn get_readme_html(app: AppHandle, language: String) -> Result<String, String> {
|
||||
// 构建 README 文件路径
|
||||
// 在开发模式下,从项目根目录读取 README.md
|
||||
|
||||
+19
-2
@@ -33,6 +33,8 @@ pub mod timeout_config;
|
||||
pub mod utils;
|
||||
mod vo;
|
||||
pub mod websocket;
|
||||
#[cfg(target_os = "ios")]
|
||||
mod webview_helper;
|
||||
|
||||
use crate::command::request_command::{im_request_command, login_command};
|
||||
use crate::command::room_member_command::{
|
||||
@@ -71,7 +73,9 @@ use crate::command::file_manager_command::{
|
||||
use crate::command::message_command::{page_msg, save_msg, send_msg, update_message_recall_status};
|
||||
use crate::command::message_mark_command::save_message_mark;
|
||||
|
||||
use tauri::{AppHandle, Listener, Manager};
|
||||
use tauri::{AppHandle, Manager};
|
||||
#[cfg(desktop)]
|
||||
use tauri::Listener;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
pub fn run() {
|
||||
@@ -301,7 +305,16 @@ fn setup_mobile() {
|
||||
if let Err(e) = tauri::Builder::default()
|
||||
.init_plugin()
|
||||
.setup(move |app| {
|
||||
common_setup(app.handle().clone())?;
|
||||
let app_handle = app.handle().clone();
|
||||
#[cfg(target_os = "ios")]
|
||||
{
|
||||
if let Some(webview_window) = app_handle.get_webview_window("mobile-home") {
|
||||
webview_helper::initialize_keyboard_adjustment(&webview_window);
|
||||
} else {
|
||||
tracing::warn!("Mobile home webview window not found during setup");
|
||||
}
|
||||
}
|
||||
common_setup(app_handle)?;
|
||||
tracing::info!("Mobile application setup completed successfully");
|
||||
Ok(())
|
||||
})
|
||||
@@ -359,6 +372,8 @@ fn get_invoke_handlers() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Se
|
||||
use crate::desktops::common_cmd::set_badge_count;
|
||||
#[cfg(mobile)]
|
||||
use crate::mobiles::splash::hide_splash_screen;
|
||||
#[cfg(target_os = "ios")]
|
||||
use crate::mobiles::keyboard::set_webview_keyboard_adjustment;
|
||||
use crate::websocket::commands::{
|
||||
ws_disconnect, ws_force_reconnect, ws_get_app_background_state, ws_get_health,
|
||||
ws_get_state, ws_init_connection, ws_is_connected, ws_send_message,
|
||||
@@ -440,5 +455,7 @@ fn get_invoke_handlers() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Se
|
||||
set_complete,
|
||||
#[cfg(mobile)]
|
||||
hide_splash_screen,
|
||||
#[cfg(target_os = "ios")]
|
||||
set_webview_keyboard_adjustment,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#[cfg(target_os = "ios")]
|
||||
#[tauri::command]
|
||||
pub fn set_webview_keyboard_adjustment(enabled: bool) {
|
||||
crate::webview_helper::set_keyboard_adjustment(enabled);
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod init;
|
||||
pub mod splash;
|
||||
pub mod keyboard;
|
||||
|
||||
@@ -0,0 +1,542 @@
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
ptr::NonNull,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use objc2::{
|
||||
define_class, msg_send,
|
||||
rc::Retained,
|
||||
runtime::{AnyObject, ProtocolObject},
|
||||
DefinedClass, MainThreadMarker, MainThreadOnly,
|
||||
};
|
||||
use objc2_core_foundation::{CGPoint, CGRect};
|
||||
use objc2_foundation::{
|
||||
NSDictionary, NSNumber, NSNotification, NSNotificationCenter, NSNotificationName, NSObject,
|
||||
NSObjectProtocol, NSString, NSTimeInterval, NSValue,
|
||||
};
|
||||
use objc2_ui_kit::{
|
||||
UIKeyboardDidShowNotification, UIKeyboardWillHideNotification, UIKeyboardWillShowNotification,
|
||||
UIScrollView, UIScrollViewContentInsetAdjustmentBehavior, UIScrollViewDelegate, UIWebView,
|
||||
UIColor, UIView, UIViewAnimationOptions,
|
||||
};
|
||||
use tauri::WebviewWindow;
|
||||
|
||||
thread_local! {
|
||||
static KEYBOARD_DELEGATE: RefCell<Option<KeyboardDelegateHolder>> = RefCell::new(None);
|
||||
}
|
||||
|
||||
static SHOULD_ADJUST: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[inline]
|
||||
fn should_adjust() -> bool {
|
||||
SHOULD_ADJUST.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum KeyboardHandlingMode {
|
||||
None,
|
||||
Adjust,
|
||||
Lock,
|
||||
}
|
||||
|
||||
enum KeyboardDelegateHolder {
|
||||
Adjust(Retained<KeyboardScrollPreventDelegate>),
|
||||
Lock(Retained<KeyboardLockDelegate>),
|
||||
}
|
||||
|
||||
pub fn initialize_keyboard_adjustment(webview_window: &WebviewWindow) {
|
||||
let _ = webview_window.with_webview(|webview| unsafe {
|
||||
#[allow(deprecated)]
|
||||
let webview: &UIWebView = &*webview.inner().cast();
|
||||
let notification_center = NSNotificationCenter::defaultCenter();
|
||||
let main_thread = MainThreadMarker::from(webview);
|
||||
let background_color =
|
||||
UIColor::colorWithRed_green_blue_alpha(243.0 / 255.0, 251.0 / 255.0, 248.0 / 255.0, 1.0);
|
||||
|
||||
webview.setOpaque(false);
|
||||
webview.setBackgroundColor(Some(&background_color));
|
||||
|
||||
let scroll_view_arc = Arc::new(webview.scrollView());
|
||||
scroll_view_arc.setBackgroundColor(Some(&background_color));
|
||||
let old_delegate_arc = Arc::new(Mutex::new(None));
|
||||
let handling_mode_arc = Arc::new(Mutex::new(KeyboardHandlingMode::None));
|
||||
|
||||
scroll_view_arc
|
||||
.setContentInsetAdjustmentBehavior(UIScrollViewContentInsetAdjustmentBehavior::Never);
|
||||
|
||||
// 记录原始状态,避免累积调整
|
||||
let keyboard_height_arc = Arc::new(Mutex::new(0f64));
|
||||
let original_frame_arc = Arc::new(Mutex::new(webview.frame()));
|
||||
let original_inset_arc = Arc::new(Mutex::new(scroll_view_arc.contentInset()));
|
||||
let webview_arc = Arc::new(
|
||||
Retained::retain(webview as *const _ as *mut UIWebView)
|
||||
.expect("Failed to retain UIWebView reference"),
|
||||
);
|
||||
|
||||
let scroll_view_arc_observer = scroll_view_arc.clone();
|
||||
let old_delegate_arc_will_show = old_delegate_arc.clone();
|
||||
let keyboard_height_arc_observer = keyboard_height_arc.clone();
|
||||
let original_frame_arc_observer = original_frame_arc.clone();
|
||||
let original_inset_arc_observer = original_inset_arc.clone();
|
||||
let handling_mode_arc_observer = handling_mode_arc.clone();
|
||||
let webview_arc_observer = webview_arc.clone();
|
||||
create_observer(
|
||||
¬ification_center,
|
||||
&UIKeyboardWillShowNotification,
|
||||
move |notification| {
|
||||
let desired_mode = if should_adjust() {
|
||||
KeyboardHandlingMode::Adjust
|
||||
} else {
|
||||
KeyboardHandlingMode::Lock
|
||||
};
|
||||
|
||||
{
|
||||
let mut mode = handling_mode_arc_observer.lock().unwrap();
|
||||
*mode = desired_mode;
|
||||
}
|
||||
|
||||
let mut old_delegate = old_delegate_arc_will_show.lock().unwrap();
|
||||
*old_delegate = scroll_view_arc_observer.delegate();
|
||||
|
||||
match desired_mode {
|
||||
KeyboardHandlingMode::Adjust => {
|
||||
unsafe {
|
||||
let scroll_view_ptr = (&**scroll_view_arc_observer)
|
||||
as *const UIScrollView
|
||||
as *mut UIScrollView;
|
||||
let _: () = msg_send![
|
||||
scroll_view_ptr,
|
||||
setShowsVerticalScrollIndicator: true
|
||||
];
|
||||
let _: () = msg_send![
|
||||
scroll_view_ptr,
|
||||
setShowsHorizontalScrollIndicator: true
|
||||
];
|
||||
let _: () = msg_send![scroll_view_ptr, setBounces: true];
|
||||
}
|
||||
|
||||
let new_delegate = KeyboardScrollPreventDelegate::new(
|
||||
main_thread,
|
||||
scroll_view_arc_observer.clone(),
|
||||
scroll_view_arc_observer.contentOffset(),
|
||||
);
|
||||
|
||||
KEYBOARD_DELEGATE.with(|cell| {
|
||||
*cell.borrow_mut() = Some(KeyboardDelegateHolder::Adjust(new_delegate));
|
||||
});
|
||||
|
||||
KEYBOARD_DELEGATE.with(|cell| {
|
||||
if let Some(KeyboardDelegateHolder::Adjust(delegate)) =
|
||||
cell.borrow().as_ref()
|
||||
{
|
||||
let delegate_obj = ProtocolObject::from_ref(&**delegate);
|
||||
scroll_view_arc_observer.setDelegate(Some(delegate_obj));
|
||||
}
|
||||
});
|
||||
}
|
||||
KeyboardHandlingMode::Lock => {
|
||||
let new_delegate = KeyboardLockDelegate::new(
|
||||
main_thread,
|
||||
scroll_view_arc_observer.clone(),
|
||||
webview_arc_observer.clone(),
|
||||
scroll_view_arc_observer.contentOffset(),
|
||||
);
|
||||
|
||||
KEYBOARD_DELEGATE.with(|cell| {
|
||||
*cell.borrow_mut() = Some(KeyboardDelegateHolder::Lock(new_delegate));
|
||||
});
|
||||
|
||||
KEYBOARD_DELEGATE.with(|cell| {
|
||||
if let Some(KeyboardDelegateHolder::Lock(delegate)) =
|
||||
cell.borrow().as_ref()
|
||||
{
|
||||
let delegate_obj = ProtocolObject::from_ref(&**delegate);
|
||||
scroll_view_arc_observer.setDelegate(Some(delegate_obj));
|
||||
}
|
||||
});
|
||||
|
||||
unsafe {
|
||||
let scroll_view_ptr = (&**scroll_view_arc_observer)
|
||||
as *const UIScrollView
|
||||
as *mut UIScrollView;
|
||||
let _: () = msg_send![
|
||||
scroll_view_ptr,
|
||||
setShowsVerticalScrollIndicator: false
|
||||
];
|
||||
let _: () = msg_send![
|
||||
scroll_view_ptr,
|
||||
setShowsHorizontalScrollIndicator: false
|
||||
];
|
||||
let _: () = msg_send![scroll_view_ptr, setBounces: false];
|
||||
}
|
||||
|
||||
// 清理键盘高度,避免误触发还原逻辑
|
||||
let mut keyboard_height = keyboard_height_arc_observer.lock().unwrap();
|
||||
*keyboard_height = 0.0;
|
||||
|
||||
// 在锁定模式下不需要执行下方的高度调整逻辑
|
||||
return;
|
||||
}
|
||||
KeyboardHandlingMode::None => unreachable!(),
|
||||
};
|
||||
|
||||
let user_info: *mut NSDictionary<NSString, AnyObject> =
|
||||
msg_send![notification, userInfo];
|
||||
if user_info.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let frame_key = NSString::from_str("UIKeyboardFrameEndUserInfoKey");
|
||||
let value: *mut NSValue = msg_send![user_info, objectForKey: &*frame_key];
|
||||
if value.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyboard_rect: CGRect = msg_send![value, CGRectValue];
|
||||
let new_keyboard_height = keyboard_rect.size.height;
|
||||
|
||||
let original_frame = *original_frame_arc_observer.lock().unwrap();
|
||||
let mut adjusted_frame = original_frame;
|
||||
adjusted_frame.size.height -= new_keyboard_height;
|
||||
|
||||
let original_inset = *original_inset_arc_observer.lock().unwrap();
|
||||
let mut adjusted_inset = original_inset;
|
||||
adjusted_inset.bottom += new_keyboard_height;
|
||||
|
||||
{
|
||||
let mut keyboard_height = keyboard_height_arc_observer.lock().unwrap();
|
||||
*keyboard_height = new_keyboard_height as f64;
|
||||
}
|
||||
|
||||
let duration_key = NSString::from_str("UIKeyboardAnimationDurationUserInfoKey");
|
||||
let duration_value: *mut NSNumber =
|
||||
msg_send![user_info, objectForKey: &*duration_key];
|
||||
let duration: NSTimeInterval = {
|
||||
duration_value
|
||||
.as_ref()
|
||||
.map(|number| number.doubleValue() as NSTimeInterval)
|
||||
.unwrap_or(0.25)
|
||||
};
|
||||
|
||||
let curve_key = NSString::from_str("UIKeyboardAnimationCurveUserInfoKey");
|
||||
let curve_value: *mut NSNumber =
|
||||
msg_send![user_info, objectForKey: &*curve_key];
|
||||
let curve_bits = {
|
||||
curve_value
|
||||
.as_ref()
|
||||
.map(|number| (number.integerValue() as u64) << 16)
|
||||
.unwrap_or(UIViewAnimationOptions::CurveEaseInOut.bits() as u64)
|
||||
};
|
||||
|
||||
let mut options = UIViewAnimationOptions::BeginFromCurrentState
|
||||
| UIViewAnimationOptions::AllowUserInteraction;
|
||||
options |= UIViewAnimationOptions::from_bits_retain(curve_bits as _);
|
||||
|
||||
let scroll_view_for_animation = scroll_view_arc_observer.clone();
|
||||
let animation_block = block2::StackBlock::new(move || {
|
||||
// Match the keyboard transition to avoid visible jumps.
|
||||
scroll_view_for_animation.setContentInset(adjusted_inset);
|
||||
scroll_view_for_animation.setScrollIndicatorInsets(adjusted_inset);
|
||||
webview.setFrame(adjusted_frame);
|
||||
})
|
||||
.copy();
|
||||
|
||||
unsafe {
|
||||
UIView::animateWithDuration_delay_options_animations_completion(
|
||||
duration,
|
||||
0.0,
|
||||
options,
|
||||
&animation_block,
|
||||
None,
|
||||
main_thread,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let scroll_view_arc_observer = scroll_view_arc.clone();
|
||||
let old_delegate_arc_will_hide = old_delegate_arc.clone();
|
||||
let keyboard_height_arc_observer = keyboard_height_arc.clone();
|
||||
let original_frame_arc_observer = original_frame_arc.clone();
|
||||
let original_inset_arc_observer = original_inset_arc.clone();
|
||||
let handling_mode_arc_observer = handling_mode_arc.clone();
|
||||
let webview_arc_observer = webview_arc.clone();
|
||||
create_observer(
|
||||
¬ification_center,
|
||||
&UIKeyboardWillHideNotification,
|
||||
move |notification| {
|
||||
let current_mode = *handling_mode_arc_observer.lock().unwrap();
|
||||
|
||||
if current_mode == KeyboardHandlingMode::Adjust {
|
||||
let keyboard_height = keyboard_height_arc_observer.lock().unwrap();
|
||||
if *keyboard_height <= 0.0 {
|
||||
return;
|
||||
}
|
||||
drop(keyboard_height);
|
||||
|
||||
let user_info: *mut NSDictionary<NSString, AnyObject> =
|
||||
msg_send![notification, userInfo];
|
||||
|
||||
let duration_key = NSString::from_str("UIKeyboardAnimationDurationUserInfoKey");
|
||||
let duration_value: *mut NSNumber =
|
||||
msg_send![user_info, objectForKey: &*duration_key];
|
||||
let duration: NSTimeInterval = {
|
||||
duration_value
|
||||
.as_ref()
|
||||
.map(|number| number.doubleValue() as NSTimeInterval)
|
||||
.unwrap_or(0.25)
|
||||
};
|
||||
|
||||
let curve_key = NSString::from_str("UIKeyboardAnimationCurveUserInfoKey");
|
||||
let curve_value: *mut NSNumber =
|
||||
msg_send![user_info, objectForKey: &*curve_key];
|
||||
let curve_bits = {
|
||||
curve_value
|
||||
.as_ref()
|
||||
.map(|number| (number.integerValue() as u64) << 16)
|
||||
.unwrap_or(UIViewAnimationOptions::CurveEaseInOut.bits() as u64)
|
||||
};
|
||||
|
||||
let mut options = UIViewAnimationOptions::BeginFromCurrentState
|
||||
| UIViewAnimationOptions::AllowUserInteraction;
|
||||
options |= UIViewAnimationOptions::from_bits_retain(curve_bits as _);
|
||||
|
||||
let original_frame = *original_frame_arc_observer.lock().unwrap();
|
||||
let original_inset = *original_inset_arc_observer.lock().unwrap();
|
||||
|
||||
let scroll_view_for_animation = scroll_view_arc_observer.clone();
|
||||
let animation_block = block2::StackBlock::new(move || {
|
||||
// 跟随键盘动画恢复初始布局
|
||||
scroll_view_for_animation.setContentInset(original_inset);
|
||||
scroll_view_for_animation.setScrollIndicatorInsets(original_inset);
|
||||
webview.setFrame(original_frame);
|
||||
})
|
||||
.copy();
|
||||
|
||||
unsafe {
|
||||
UIView::animateWithDuration_delay_options_animations_completion(
|
||||
duration,
|
||||
0.0,
|
||||
options,
|
||||
&animation_block,
|
||||
None,
|
||||
main_thread,
|
||||
);
|
||||
}
|
||||
|
||||
let mut keyboard_height = keyboard_height_arc_observer.lock().unwrap();
|
||||
*keyboard_height = 0.0;
|
||||
let mut mode = handling_mode_arc_observer.lock().unwrap();
|
||||
*mode = KeyboardHandlingMode::None;
|
||||
} else if current_mode == KeyboardHandlingMode::Lock {
|
||||
// 在锁定模式下,恢复滚动视图的默认行为并保持键盘隐藏
|
||||
let webview_ptr =
|
||||
(&**webview_arc_observer) as *const UIWebView as *mut UIWebView;
|
||||
let _: () = msg_send![webview_ptr, endEditing: true];
|
||||
|
||||
unsafe {
|
||||
let scroll_view_ptr = (&**scroll_view_arc_observer)
|
||||
as *const UIScrollView
|
||||
as *mut UIScrollView;
|
||||
let _: () = msg_send![
|
||||
scroll_view_ptr,
|
||||
setShowsVerticalScrollIndicator: true
|
||||
];
|
||||
let _: () = msg_send![
|
||||
scroll_view_ptr,
|
||||
setShowsHorizontalScrollIndicator: true
|
||||
];
|
||||
let _: () = msg_send![scroll_view_ptr, setBounces: true];
|
||||
}
|
||||
|
||||
KEYBOARD_DELEGATE.with(|cell| {
|
||||
*cell.borrow_mut() = None;
|
||||
});
|
||||
|
||||
let mut old_delegate = old_delegate_arc_will_hide.lock().unwrap();
|
||||
if let Some(delegate) = old_delegate.take() {
|
||||
scroll_view_arc_observer.setDelegate(Some(delegate.as_ref()));
|
||||
} else {
|
||||
scroll_view_arc_observer.setDelegate(None);
|
||||
}
|
||||
|
||||
let mut mode = handling_mode_arc_observer.lock().unwrap();
|
||||
*mode = KeyboardHandlingMode::None;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let scroll_view_arc_observer = scroll_view_arc.clone();
|
||||
let old_delegate_arc_did_show = old_delegate_arc.clone();
|
||||
let keyboard_height_arc_observer = keyboard_height_arc.clone();
|
||||
let original_frame_arc_observer = original_frame_arc.clone();
|
||||
let original_inset_arc_observer = original_inset_arc.clone();
|
||||
let handling_mode_arc_observer = handling_mode_arc.clone();
|
||||
create_observer(
|
||||
¬ification_center,
|
||||
&UIKeyboardDidShowNotification,
|
||||
move |notification| {
|
||||
let current_mode = *handling_mode_arc_observer.lock().unwrap();
|
||||
if current_mode != KeyboardHandlingMode::Adjust {
|
||||
return;
|
||||
}
|
||||
|
||||
let user_info: *mut NSDictionary<NSString, AnyObject> = msg_send![notification, userInfo];
|
||||
if user_info.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let key = NSString::from_str("UIKeyboardFrameEndUserInfoKey");
|
||||
let value: *mut NSValue = msg_send![user_info, objectForKey: &*key];
|
||||
if value.is_null() {
|
||||
return;
|
||||
}
|
||||
|
||||
let keyboard_rect: CGRect = msg_send![value, CGRectValue];
|
||||
let new_keyboard_height = keyboard_rect.size.height;
|
||||
|
||||
// 基于原始状态进行调整,避免累积
|
||||
let original_frame = original_frame_arc_observer.lock().unwrap();
|
||||
let mut adjusted_frame = *original_frame;
|
||||
adjusted_frame.size.height -= new_keyboard_height;
|
||||
webview.setFrame(adjusted_frame);
|
||||
|
||||
// 基于原始 inset 进行调整
|
||||
let original_inset = original_inset_arc_observer.lock().unwrap();
|
||||
let mut adjusted_inset = *original_inset;
|
||||
adjusted_inset.bottom += new_keyboard_height;
|
||||
scroll_view_arc_observer.setContentInset(adjusted_inset);
|
||||
scroll_view_arc_observer.setScrollIndicatorInsets(adjusted_inset);
|
||||
|
||||
// 更新键盘高度记录
|
||||
let mut keyboard_height = keyboard_height_arc_observer.lock().unwrap();
|
||||
*keyboard_height = new_keyboard_height;
|
||||
|
||||
let mut old_delegate = old_delegate_arc_did_show.lock().unwrap();
|
||||
|
||||
if let Some(delegate) = old_delegate.take() {
|
||||
scroll_view_arc_observer.setDelegate(Some(delegate.as_ref()));
|
||||
} else {
|
||||
scroll_view_arc_observer.setDelegate(None);
|
||||
}
|
||||
|
||||
KEYBOARD_DELEGATE.with(|cell| {
|
||||
*cell.borrow_mut() = None;
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_keyboard_adjustment(enabled: bool) {
|
||||
SHOULD_ADJUST.store(enabled, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub struct KeyboardScrollPreventDelegateState {
|
||||
pub scroll_view: Arc<Retained<UIScrollView>>,
|
||||
pub offset: CGPoint,
|
||||
}
|
||||
|
||||
define_class! {
|
||||
#[unsafe(super(NSObject))]
|
||||
#[thread_kind = MainThreadOnly]
|
||||
#[name = "KeyboardScrollPreventDelegate"]
|
||||
#[ivars = KeyboardScrollPreventDelegateState]
|
||||
pub(crate) struct KeyboardScrollPreventDelegate;
|
||||
|
||||
unsafe impl NSObjectProtocol for KeyboardScrollPreventDelegate {}
|
||||
|
||||
unsafe impl UIScrollViewDelegate for KeyboardScrollPreventDelegate {
|
||||
#[unsafe(method(scrollViewDidScroll:))]
|
||||
unsafe fn scroll_view_did_scroll(&self, _scroll_view: &UIScrollView) {
|
||||
self.ivars()
|
||||
.scroll_view
|
||||
.setContentOffset(self.ivars().offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardScrollPreventDelegate {
|
||||
fn new(
|
||||
mtm: MainThreadMarker,
|
||||
scroll_view: Arc<Retained<UIScrollView>>,
|
||||
offset: CGPoint,
|
||||
) -> Retained<Self> {
|
||||
let delegate = Self::alloc(mtm).set_ivars(KeyboardScrollPreventDelegateState {
|
||||
scroll_view,
|
||||
offset,
|
||||
});
|
||||
|
||||
unsafe { msg_send![super(delegate), init] }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeyboardLockDelegateState {
|
||||
pub scroll_view: Arc<Retained<UIScrollView>>,
|
||||
pub webview: Arc<Retained<UIWebView>>,
|
||||
pub locked_offset: CGPoint,
|
||||
}
|
||||
|
||||
define_class! {
|
||||
#[unsafe(super(NSObject))]
|
||||
#[thread_kind = MainThreadOnly]
|
||||
#[name = "KeyboardLockDelegate"]
|
||||
#[ivars = KeyboardLockDelegateState]
|
||||
pub(crate) struct KeyboardLockDelegate;
|
||||
|
||||
unsafe impl NSObjectProtocol for KeyboardLockDelegate {}
|
||||
|
||||
unsafe impl UIScrollViewDelegate for KeyboardLockDelegate {
|
||||
#[unsafe(method(scrollViewWillBeginDragging:))]
|
||||
unsafe fn scroll_view_will_begin_dragging(&self, _scroll_view: &UIScrollView) {
|
||||
let ivars = self.ivars();
|
||||
let webview_ptr =
|
||||
(&**ivars.webview) as *const UIWebView as *mut UIWebView;
|
||||
let _: () = msg_send![webview_ptr, endEditing: true];
|
||||
ivars
|
||||
.scroll_view
|
||||
.setContentOffset(ivars.locked_offset);
|
||||
}
|
||||
|
||||
#[unsafe(method(scrollViewDidScroll:))]
|
||||
unsafe fn scroll_view_did_scroll(&self, _scroll_view: &UIScrollView) {
|
||||
let ivars = self.ivars();
|
||||
ivars
|
||||
.scroll_view
|
||||
.setContentOffset(ivars.locked_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardLockDelegate {
|
||||
fn new(
|
||||
mtm: MainThreadMarker,
|
||||
scroll_view: Arc<Retained<UIScrollView>>,
|
||||
webview: Arc<Retained<UIWebView>>,
|
||||
offset: CGPoint,
|
||||
) -> Retained<Self> {
|
||||
let delegate = Self::alloc(mtm).set_ivars(KeyboardLockDelegateState {
|
||||
scroll_view,
|
||||
webview,
|
||||
locked_offset: offset,
|
||||
});
|
||||
|
||||
unsafe { msg_send![super(delegate), init] }
|
||||
}
|
||||
}
|
||||
|
||||
fn create_observer(
|
||||
center: &NSNotificationCenter,
|
||||
name: &NSNotificationName,
|
||||
handler: impl Fn(&NSNotification) + 'static,
|
||||
) -> Retained<ProtocolObject<dyn NSObjectProtocol>> {
|
||||
let block = block2::RcBlock::new(move |notification: NonNull<NSNotification>| {
|
||||
handler(unsafe { notification.as_ref() });
|
||||
});
|
||||
|
||||
unsafe { center.addObserverForName_object_queue_usingBlock(Some(name), None, None, &block) }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#[cfg(target_os = "ios")]
|
||||
pub mod ios;
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub use ios::{initialize_keyboard_adjustment, set_keyboard_adjustment};
|
||||
+17
-1
@@ -31,7 +31,7 @@ import { useMitt } from '@/hooks/useMitt.ts'
|
||||
import { useWindow } from '@/hooks/useWindow.ts'
|
||||
import { useGlobalStore } from '@/stores/global'
|
||||
import { useSettingStore } from '@/stores/setting.ts'
|
||||
import { isDesktop, isMobile, isWindows } from '@/utils/PlatformConstants'
|
||||
import { isDesktop, isIOS, isMobile, isWindows } from '@/utils/PlatformConstants'
|
||||
import LockScreen from '@/views/LockScreen.vue'
|
||||
|
||||
import {
|
||||
@@ -382,7 +382,23 @@ const listenMobileReLogin = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iOS网络权限预请求
|
||||
* 在应用启动时发起一个轻量级网络请求,触发iOS的网络权限弹窗
|
||||
*/
|
||||
const requestNetworkPermissionForIOS = async () => {
|
||||
if (!isIOS()) return
|
||||
|
||||
await fetch('https://www.apple.com/favicon.ico', {
|
||||
method: 'HEAD',
|
||||
cache: 'no-cache'
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// iOS应用启动时预请求网络权限(必须在最开始执行)
|
||||
requestNetworkPermissionForIOS()
|
||||
|
||||
// 仅在windows上使用
|
||||
if (isWindows()) {
|
||||
fixedScale.enable()
|
||||
|
||||
@@ -94,7 +94,7 @@ export const useReplaceMsg = () => {
|
||||
*/
|
||||
const getMessageSenderName = (message: MessageType, defaultName: string = '', roomId: string) => {
|
||||
if (!message?.fromUser?.uid) return defaultName
|
||||
const session = chatStore.getSession(roomId)
|
||||
const session = chatStore.getSession(roomId)!
|
||||
if (session.type === RoomTypeEnum.GROUP) {
|
||||
const user = groupStore.getUser(roomId, message.fromUser.uid)
|
||||
return user?.myName || user?.name || ''
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import 'vant/es/dialog/style'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { useGlobalStore } from '@/stores/global'
|
||||
import { isIOS } from '@/utils/PlatformConstants'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
const emit = defineEmits(['focus', 'blur', 'updateHeight'])
|
||||
@@ -38,6 +40,16 @@ onMounted(() => {
|
||||
resizeObserver.disconnect()
|
||||
})
|
||||
}
|
||||
|
||||
if (isIOS()) {
|
||||
invoke('set_webview_keyboard_adjustment', { enabled: true })
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (isIOS()) {
|
||||
invoke('set_webview_keyboard_adjustment', { enabled: false })
|
||||
}
|
||||
})
|
||||
|
||||
// ==== 对外暴露 ====
|
||||
|
||||
+20
-7
@@ -116,12 +116,7 @@
|
||||
</n-button>
|
||||
|
||||
<!-- 协议 -->
|
||||
<n-flex
|
||||
align="center"
|
||||
justify="center"
|
||||
:style="isAndroid() ? { bottom: safeArea.bottom + 10 + 'px' } : {}"
|
||||
:size="6"
|
||||
class="absolute bottom-0 w-[80%]">
|
||||
<n-flex align="center" justify="center" :style="agreementStyle" :size="6" class="absolute bottom-0 w-[80%]">
|
||||
<n-checkbox v-model:checked="protocol" />
|
||||
<div class="text-12px color-#909090 cursor-default lh-14px">
|
||||
<span>已阅读并同意</span>
|
||||
@@ -270,6 +265,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { debounce } from 'lodash-es'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import Validation from '@/components/common/Validation.vue'
|
||||
import router from '@/router'
|
||||
import type { RegisterUserReq, UserInfoType } from '@/services/types'
|
||||
@@ -277,7 +273,7 @@ import { useLoginHistoriesStore } from '@/stores/loginHistory.ts'
|
||||
import { useMobileStore } from '@/stores/mobile'
|
||||
import { AvatarUtils } from '@/utils/AvatarUtils'
|
||||
import { register, sendCaptcha } from '@/utils/ImRequestUtils'
|
||||
import { isAndroid } from '@/utils/PlatformConstants'
|
||||
import { isAndroid, isIOS } from '@/utils/PlatformConstants'
|
||||
import { validateAlphaNumeric, validateSpecialChar } from '@/utils/Validate'
|
||||
import { useMitt } from '../hooks/useMitt'
|
||||
import { WsResponseMessageType } from '../services/wsType'
|
||||
@@ -347,6 +343,17 @@ const sendCodeDisabled = computed(() => {
|
||||
return sendCodeLoading.value || sendCodeCountdown.value > 0 || !registerInfo.value.email || !isEmailValid.value
|
||||
})
|
||||
|
||||
const agreementStyle = computed(() => {
|
||||
const inset = safeArea.value.bottom || 0
|
||||
if (isAndroid()) {
|
||||
return { bottom: `${inset + 10}px` }
|
||||
}
|
||||
if (inset > 0) {
|
||||
return { bottom: `${inset}px` }
|
||||
}
|
||||
return { bottom: 'var(--safe-area-inset-bottom)' }
|
||||
})
|
||||
|
||||
const stopSendCodeCountdown = () => {
|
||||
timerWorker.postMessage({
|
||||
type: 'clearTimer',
|
||||
@@ -674,6 +681,9 @@ const refreshAvatar = debounce((newAccount: string) => {
|
||||
|
||||
onMounted(async () => {
|
||||
window.addEventListener('click', closeMenu, true)
|
||||
if (isIOS()) {
|
||||
invoke('set_webview_keyboard_adjustment', { enabled: false })
|
||||
}
|
||||
// 只有在需要登录的情况下才显示登录窗口
|
||||
if (isJumpDirectly.value) {
|
||||
loading.value = false
|
||||
@@ -697,6 +707,9 @@ onUnmounted(() => {
|
||||
window.removeEventListener('click', closeMenu, true)
|
||||
stopSendCodeCountdown()
|
||||
timerWorker.terminate()
|
||||
if (isIOS()) {
|
||||
invoke('set_webview_keyboard_adjustment', { enabled: false })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -120,21 +120,4 @@ $font-family-sans: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-family: $font-family-system, $font-family-windows, $font-family-sans;
|
||||
color: #757775;
|
||||
}
|
||||
|
||||
// .page-view {
|
||||
// flex: 1;
|
||||
// // 进入时的动画
|
||||
// animation: fade-slide-in 0.3s ease;
|
||||
// }
|
||||
|
||||
// @keyframes fade-slide-in {
|
||||
// from {
|
||||
// transform: translateX(20px);
|
||||
// opacity: 0;
|
||||
// }
|
||||
// to {
|
||||
// transform: translateX(0);
|
||||
// opacity: 1;
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
||||
+1
-5
@@ -3,7 +3,7 @@ import { info } from '@tauri-apps/plugin-log'
|
||||
import { sendNotification } from '@tauri-apps/plugin-notification'
|
||||
import { defineStore } from 'pinia'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { AppException, ErrorType } from '@/common/exception'
|
||||
import { ErrorType } from '@/common/exception'
|
||||
import { type MessageStatusEnum, MsgEnum, RoomTypeEnum, StoresEnum, TauriCommand } from '@/enums'
|
||||
import type { MarkItemType, MessageType, RevokedMsgType, SessionItem } from '@/services/types'
|
||||
import { useGlobalStore } from '@/stores/global.ts'
|
||||
@@ -320,10 +320,6 @@ export const useChatStore = defineStore(
|
||||
// 通过房间ID获取会话信息
|
||||
const getSession = (roomId: string) => {
|
||||
const currentSession = roomId ? sessionList.value.find((item) => item.roomId === roomId) : sessionList.value[0]
|
||||
if (!currentSession) {
|
||||
throw new AppException(`根据房间 ID: ${roomId}无法找到会话`)
|
||||
}
|
||||
|
||||
return currentSession
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ export const useGlobalStore = defineStore(
|
||||
const currentSessionRoomId = ref('')
|
||||
// 当前会话信息:包含房间ID和房间类型
|
||||
const currentSession = computed((): SessionItem => {
|
||||
return chatStore.getSession(currentSessionRoomId.value)
|
||||
return chatStore.getSession(currentSessionRoomId.value)!
|
||||
})
|
||||
|
||||
/** 当前选中的联系人信息 */
|
||||
|
||||
Reference in New Issue
Block a user