feat: zap modal
This commit is contained in:
parent
93e7ee18c1
commit
73d03ca0f1
656
Cargo.lock
generated
656
Cargo.lock
generated
@ -137,6 +137,17 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
@ -886,6 +897,16 @@ version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
|
||||
|
||||
[[package]]
|
||||
name = "calendrical_calculations"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27ca2b6e2f7d75f43e001ded6f25e79b80bded5abbe764cbdf78c25a3051f4b"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.13.0"
|
||||
@ -1174,6 +1195,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core_maths"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3b02505ccb8c50b0aa21ace0fc08c3e53adebd4e58caa18a36152803c7709a3"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coreaudio-rs"
|
||||
version = "0.11.3"
|
||||
@ -1366,6 +1396,17 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
@ -1459,6 +1500,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui-modal"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "297fa5697b1048198cc12f5f101312a6fcae42e55a637c861a316d6028797e42"
|
||||
dependencies = [
|
||||
"egui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui-video"
|
||||
version = "0.8.0"
|
||||
@ -1567,6 +1617,15 @@ dependencies = [
|
||||
"egui_extras",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui_qr"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.v0l.io/Kieran/egui_qr.git?rev=f9cf52b7eae353fa9e59ed0358151211d48824d1#f9cf52b7eae353fa9e59ed0358151211d48824d1"
|
||||
dependencies = [
|
||||
"egui",
|
||||
"qrcode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "egui_tabs"
|
||||
version = "0.2.1"
|
||||
@ -1620,6 +1679,15 @@ version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "email_address"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1019fa28f600f5b581b7a603d515c3f1635da041ca211b5055804788673abfe"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "emath"
|
||||
version = "0.29.1"
|
||||
@ -1869,6 +1937,17 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixed_decimal"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0febbeb1118a9ecdee6e4520ead6b54882e843dd0592ad233247dbee84c53db8"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"smallvec",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flatbuffers"
|
||||
version = "23.5.26"
|
||||
@ -2374,6 +2453,399 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "icu"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff5e3018d703f168b00dcefa540a65f1bbc50754ae32f3f5f0e43fe5ee51502"
|
||||
dependencies = [
|
||||
"icu_calendar",
|
||||
"icu_casemap",
|
||||
"icu_collator",
|
||||
"icu_collections",
|
||||
"icu_datetime",
|
||||
"icu_decimal",
|
||||
"icu_experimental",
|
||||
"icu_list",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"icu_segmenter",
|
||||
"icu_timezone",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7265b2137f9a36f7634a308d91f984574bbdba8cfd95ceffe1c345552275a8ff"
|
||||
dependencies = [
|
||||
"calendrical_calculations",
|
||||
"displaydoc",
|
||||
"icu_calendar_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_calendar_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e009b7f0151ee6fb28c40b1283594397e0b7183820793e9ace3dcd13db126d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff0c8ae9f8d31b12e27fc385ff9ab1f3cd9b17417c665c49e4ec958c37da75f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_casemap_data",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_casemap_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d57966d5ab748f74513be4046867f9a20e801e2775d41f91d04a0f560b61f08"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d370371887d31d56f361c3eaa15743e54f13bc677059c9191c77e099ed6966b2"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collator_data",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collator_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ee3f88741364b7d6269cce6827a3e6a8a2cf408a78f766c9224ab479d5e4ae5"
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d115efb85e08df3fd77e77f52e7e087545a783fffba8be80bfa2102f306b1780"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"fixed_decimal",
|
||||
"icu_calendar",
|
||||
"icu_datetime_data",
|
||||
"icu_decimal",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals",
|
||||
"icu_provider",
|
||||
"icu_timezone",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_datetime_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7e7f7a01269b9afb0a39eff4f8676f693b55f509b3120e43a0350a9f88bea"
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb8fd98f86ec0448d85e1edf8884e4e318bb2e121bd733ec929a05c0a5e8b0eb"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_decimal_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_decimal_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d424c994071c6f5644f999925fc868c85fec82295326e75ad5017bc94b41523"
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "844ad7b682a165c758065d694bc4d74ac67f176da1c499a04d85d492c0f193b7"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_collections",
|
||||
"icu_decimal",
|
||||
"icu_experimental_data",
|
||||
"icu_locid",
|
||||
"icu_locid_transform",
|
||||
"icu_normalizer",
|
||||
"icu_pattern",
|
||||
"icu_plurals",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"litemap",
|
||||
"num-bigint",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_experimental_data"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c178b9a34083fca5bd70d61f647575335e9c197d0f30c38e8ccd187babc69d0"
|
||||
|
||||
[[package]]
|
||||
name = "icu_list"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbfeda1d7775b6548edd4e8b7562304a559a91ed56ab56e18961a053f367c365"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_list_data",
|
||||
"icu_locid_transform",
|
||||
"icu_provider",
|
||||
"regex-automata 0.2.0",
|
||||
"writeable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_list_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1825170d2c6679cb20dbd96a589d034e49f698aed9a2ef4fafc9a0101ed298f"
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_locid_transform_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locid_transform_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"utf16_iter",
|
||||
"utf8_iter",
|
||||
"write16",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
|
||||
|
||||
[[package]]
|
||||
name = "icu_pattern"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7f36aafd098d6717de34e668a8120822275c1fba22b936e757b7de8a2fd7e4"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"either",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a70e7c025dbd5c501b0a5c188cd11666a424f0dadcd4f0a95b7dafde3b114"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"fixed_decimal",
|
||||
"icu_locid_transform",
|
||||
"icu_plurals_data",
|
||||
"icu_provider",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_plurals_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e3e8f775b215d45838814a090a2227247a7431d74e9156407d9c37f6ef0f208"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid_transform",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"tinystr",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locid",
|
||||
"icu_provider_macros",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider_macros"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a717725612346ffc2d7b42c94b820db6908048f39434504cb130e8b46256b0de"
|
||||
dependencies = [
|
||||
"core_maths",
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locid",
|
||||
"icu_provider",
|
||||
"icu_segmenter_data",
|
||||
"utf8_iter",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_segmenter_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f739ee737260d955e330bc83fdeaaf1631f7fb7ed218761d3c04bb13bb7d79df"
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91ba6a585939a020c787235daa8aee856d9bceebd6355e283c0c310bc6de96"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_calendar",
|
||||
"icu_provider",
|
||||
"icu_timezone_data",
|
||||
"tinystr",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_timezone_data"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c588878c508a3e2ace333b3c50296053e6483c6a7541251b546cc59dcd6ced8e"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
@ -2660,6 +3132,12 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
@ -2677,12 +3155,36 @@ version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
|
||||
|
||||
[[package]]
|
||||
name = "lnurl-rs"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41eacdd87b675792f7752f3dd0937a00241a504c3956c47f72986490662e1db4"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"bech32",
|
||||
"bitcoin",
|
||||
"cbc",
|
||||
"email_address",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.12"
|
||||
@ -2993,6 +3495,7 @@ version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aad4b767bbed24ac5eb4465bfb83bc1210522eb99d67cf4e547ec2ec7e47786"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"async-trait",
|
||||
"base64 0.22.1",
|
||||
"bech32",
|
||||
@ -3828,6 +4331,12 @@ dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qrcode"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec"
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
@ -4029,6 +4538,15 @@ dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.8"
|
||||
@ -4504,6 +5022,12 @@ dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@ -4576,6 +5100,17 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
@ -4735,6 +5270,16 @@ dependencies = [
|
||||
"strict-num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
@ -5099,6 +5644,18 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf16_iter"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.11.0"
|
||||
@ -5936,6 +6493,21 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
@ -6015,6 +6587,30 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zap_stream_app"
|
||||
version = "0.1.0"
|
||||
@ -6027,10 +6623,16 @@ dependencies = [
|
||||
"directories",
|
||||
"eframe",
|
||||
"egui",
|
||||
"egui-modal",
|
||||
"egui-video",
|
||||
"egui_qr",
|
||||
"ehttp 0.5.0",
|
||||
"enostr",
|
||||
"fixed_decimal",
|
||||
"icu",
|
||||
"icu_decimal",
|
||||
"itertools 0.14.0",
|
||||
"lnurl-rs",
|
||||
"log",
|
||||
"nostr",
|
||||
"nostrdb",
|
||||
@ -6163,12 +6765,66 @@ dependencies = [
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb594dd55d87335c5f60177cee24f19457a5ec10a065e0a3014722ad252d0a1f"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.4.12"
|
||||
|
@ -20,15 +20,21 @@ itertools = "0.14.0"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
directories = "5.0.1"
|
||||
egui-video = { git = "https://github.com/v0l/egui-video.git", rev = "d2ea3b4db21eb870a207db19e4cd21c7d1d24836" }
|
||||
egui_qr = { git = "https://git.v0l.io/Kieran/egui_qr.git", rev = "f9cf52b7eae353fa9e59ed0358151211d48824d1" }
|
||||
|
||||
# notedeck stuff
|
||||
nostr = { version = "0.37.0", default-features = false, features = ["std", "nip49"] }
|
||||
nostr = { version = "0.37.0", default-features = false, features = ["std", "nip49", "nip57"] }
|
||||
nostrdb = { git = "https://github.com/damus-io/nostrdb-rs", rev = "2111948b078b24a1659d0bd5d8570f370269c99b" }
|
||||
notedeck-chrome = { git = "https://github.com/damus-io/notedeck", rev = "06417ff69e772f24ffd7fb2b025f879463d8c51f", package = "notedeck_chrome" }
|
||||
notedeck = { git = "https://github.com/damus-io/notedeck", rev = "06417ff69e772f24ffd7fb2b025f879463d8c51f", package = "notedeck" }
|
||||
enostr = { git = "https://github.com/damus-io/notedeck", rev = "06417ff69e772f24ffd7fb2b025f879463d8c51f", package = "enostr" }
|
||||
poll-promise = "0.3.0"
|
||||
ehttp = "0.5.0"
|
||||
egui-modal = "0.5.0"
|
||||
icu = "1.5.0"
|
||||
icu_decimal = "1.5.0"
|
||||
fixed_decimal = "0.5.6"
|
||||
lnurl-rs = { version = "0.9.0", default-features = false }
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
eframe = { version = "0.29.1" }
|
||||
|
15
src/app.rs
15
src/app.rs
@ -8,6 +8,8 @@ use enostr::{PoolEvent, RelayEvent, RelayMessage};
|
||||
use log::{error, info, warn};
|
||||
use nostrdb::{Filter, Transaction};
|
||||
use notedeck::AppContext;
|
||||
use poll_promise::Promise;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc;
|
||||
|
||||
pub struct ZapStreamApp {
|
||||
@ -20,6 +22,7 @@ pub struct ZapStreamApp {
|
||||
|
||||
widget: Box<dyn NostrWidget>,
|
||||
profiles: ProfileLoader,
|
||||
fetch: HashMap<String, Promise<ehttp::Result<ehttp::Response>>>,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
@ -65,6 +68,7 @@ impl ZapStreamApp {
|
||||
profiles: ProfileLoader::new(),
|
||||
routes_tx: tx,
|
||||
routes_rx: rx,
|
||||
fetch: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,12 +136,13 @@ impl notedeck::App for ZapStreamApp {
|
||||
let tx = Transaction::new(ctx.ndb).expect("transaction");
|
||||
// display app
|
||||
ui.vertical(|ui| {
|
||||
let mut svc = RouteServices {
|
||||
router: self.routes_tx.clone(),
|
||||
tx: &tx,
|
||||
egui: ui.ctx().clone(),
|
||||
let mut svc = RouteServices::new(
|
||||
ui.ctx().clone(),
|
||||
&tx,
|
||||
ctx,
|
||||
};
|
||||
self.routes_tx.clone(),
|
||||
&mut self.fetch,
|
||||
);
|
||||
Header::new().render(ui, &mut svc, &tx);
|
||||
if let Err(e) = self.widget.update(&mut svc) {
|
||||
error!("{}", e);
|
||||
|
44
src/link.rs
44
src/link.rs
@ -1,10 +1,11 @@
|
||||
use crate::note_util::NoteUtil;
|
||||
use bech32::{Hrp, NoChecksum};
|
||||
use nostr::prelude::hex;
|
||||
use nostrdb::{Filter, Note};
|
||||
use nostr::prelude::{hex, Coordinate};
|
||||
use nostr::{Kind, PublicKey};
|
||||
use nostrdb::{Filter, NdbStrVariant, Note};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct NostrLink {
|
||||
pub hrp: NostrLinkType,
|
||||
pub id: IdOrStr,
|
||||
@ -13,7 +14,7 @@ pub struct NostrLink {
|
||||
pub relays: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub enum IdOrStr {
|
||||
Id([u8; 32]),
|
||||
Str(String),
|
||||
@ -28,7 +29,7 @@ impl Display for IdOrStr {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub enum NostrLinkType {
|
||||
Note,
|
||||
PublicKey,
|
||||
@ -58,22 +59,16 @@ impl NostrLink {
|
||||
}
|
||||
|
||||
pub fn from_note(note: &Note<'_>) -> Self {
|
||||
if note.kind() >= 30_000
|
||||
&& note.kind() < 40_000
|
||||
&& note
|
||||
.get_tag_value("d")
|
||||
.and_then(|v| v.variant().str())
|
||||
.is_some()
|
||||
{
|
||||
if note.kind() >= 30_000 && note.kind() < 40_000 {
|
||||
Self {
|
||||
hrp: NostrLinkType::Coordinate,
|
||||
id: IdOrStr::Str(
|
||||
note.get_tag_value("d")
|
||||
.unwrap()
|
||||
.variant()
|
||||
.str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
.map(|t| match t.variant() {
|
||||
NdbStrVariant::Id(s) => hex::encode(s),
|
||||
NdbStrVariant::Str(s) => s.to_owned(),
|
||||
})
|
||||
.unwrap_or(String::from("")),
|
||||
),
|
||||
kind: Some(note.kind()),
|
||||
author: Some(*note.pubkey()),
|
||||
@ -171,3 +166,18 @@ impl Display for NostrLink {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<Coordinate> for NostrLink {
|
||||
type Error = ();
|
||||
|
||||
fn try_into(self) -> Result<Coordinate, Self::Error> {
|
||||
match self.hrp {
|
||||
NostrLinkType::Coordinate => Ok(Coordinate::new(
|
||||
Kind::from_u16(self.kind.unwrap() as u16),
|
||||
PublicKey::from_slice(&self.author.unwrap()).unwrap(),
|
||||
)
|
||||
.identifier(self.id.to_string())),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
104
src/route/mod.rs
104
src/route/mod.rs
@ -1,13 +1,20 @@
|
||||
use crate::link::NostrLink;
|
||||
use crate::services::ffmpeg_loader::FfmpegLoader;
|
||||
use crate::PollOption;
|
||||
use anyhow::{anyhow, bail};
|
||||
use egui::load::SizedTexture;
|
||||
use egui::{Context, Image, TextureHandle};
|
||||
use egui::{Context, Id, Image, TextureHandle};
|
||||
use ehttp::Response;
|
||||
use enostr::EventClientMessage;
|
||||
use lnurl::lightning_address::LightningAddress;
|
||||
use lnurl::pay::PayResponse;
|
||||
use lnurl::LnUrlResponse;
|
||||
use log::{info, warn};
|
||||
use nostr::{Event, EventBuilder, JsonUtil, Kind, Tag};
|
||||
use nostr::{serde_json, Event, EventBuilder, JsonUtil, Keys, Kind, SecretKey, Tag};
|
||||
use nostrdb::{NdbProfile, NoteKey, Transaction};
|
||||
use notedeck::{AppContext, ImageCache};
|
||||
use poll_promise::Promise;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::sync::mpsc;
|
||||
use std::task::Poll;
|
||||
@ -46,13 +53,31 @@ pub enum RouteAction {
|
||||
}
|
||||
|
||||
pub struct RouteServices<'a, 'ctx> {
|
||||
pub router: mpsc::Sender<RouteType>,
|
||||
pub egui: Context,
|
||||
pub tx: &'a Transaction,
|
||||
pub ctx: &'a mut AppContext<'ctx>,
|
||||
|
||||
router: mpsc::Sender<RouteType>,
|
||||
fetch: &'a mut HashMap<String, Promise<ehttp::Result<Response>>>,
|
||||
}
|
||||
|
||||
impl<'a> RouteServices<'a, '_> {
|
||||
impl<'a, 'ctx> RouteServices<'a, 'ctx> {
|
||||
pub fn new(
|
||||
egui: Context,
|
||||
tx: &'a Transaction,
|
||||
ctx: &'a mut AppContext<'ctx>,
|
||||
router: mpsc::Sender<RouteType>,
|
||||
fetch: &'a mut HashMap<String, Promise<ehttp::Result<Response>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
egui,
|
||||
tx,
|
||||
ctx,
|
||||
router,
|
||||
fetch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn navigate(&self, route: RouteType) {
|
||||
self.router.send(route).expect("route send failed");
|
||||
self.egui.request_repaint();
|
||||
@ -103,23 +128,76 @@ impl<'a> RouteServices<'a, '_> {
|
||||
Image::from_bytes(name, data)
|
||||
}
|
||||
|
||||
/// Create a poll_promise fetch
|
||||
pub fn fetch(&mut self, url: &str) -> Poll<&ehttp::Result<Response>> {
|
||||
if !self.fetch.contains_key(url) {
|
||||
let (sender, promise) = Promise::new();
|
||||
let request = ehttp::Request::get(url);
|
||||
let ctx = self.egui.clone();
|
||||
ehttp::fetch(request, move |response| {
|
||||
sender.send(response);
|
||||
ctx.request_repaint();
|
||||
});
|
||||
info!("Fetching {}", url);
|
||||
self.fetch.insert(url.to_string(), promise);
|
||||
}
|
||||
self.fetch.get(url).expect("fetch").poll()
|
||||
}
|
||||
|
||||
pub fn fetch_lnurlp(&mut self, pubkey: &[u8; 32]) -> anyhow::Result<Poll<PayResponse>> {
|
||||
let target = self
|
||||
.profile(pubkey)
|
||||
.and_then(|p| p.lud16())
|
||||
.ok_or(anyhow!("No lightning address found"))?;
|
||||
|
||||
let addr = LightningAddress::new(target)?;
|
||||
match self.fetch(&addr.lnurlp_url()) {
|
||||
Poll::Ready(Ok(r)) => {
|
||||
if r.ok {
|
||||
let rsp: PayResponse = serde_json::from_slice(&r.bytes)?;
|
||||
Ok(Poll::Ready(rsp))
|
||||
} else {
|
||||
bail!("Invalid response code {}", r.status);
|
||||
}
|
||||
}
|
||||
Poll::Ready(Err(e)) => Err(anyhow!("{}", e)),
|
||||
Poll::Pending => Ok(Poll::Pending),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_live_chat_msg(&self, link: &NostrLink, msg: &str) -> Option<Event> {
|
||||
if msg.is_empty() {
|
||||
return None;
|
||||
}
|
||||
if let Some(acc) = self.ctx.accounts.get_selected_account() {
|
||||
if let Some(key) = &acc.secret_key {
|
||||
let nostr_key =
|
||||
nostr::Keys::new(nostr::SecretKey::from_slice(key.as_secret_bytes()).unwrap());
|
||||
return EventBuilder::new(Kind::LiveEventMessage, msg)
|
||||
if let Some(key) = self.current_account_keys() {
|
||||
EventBuilder::new(Kind::LiveEventMessage, msg)
|
||||
.tag(Tag::parse(link.to_tag()).unwrap())
|
||||
.sign_with_keys(&nostr_key)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
.sign_with_keys(&key)
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_account_keys(&self) -> Option<Keys> {
|
||||
self.ctx
|
||||
.accounts
|
||||
.get_selected_account()
|
||||
.and_then(|acc| acc.secret_key.as_ref().map(|k| Keys::new(k.clone())))
|
||||
}
|
||||
|
||||
/// Simple wrapper around egui temp data
|
||||
pub fn get<T: Clone + 'static>(&self, k: &str) -> Option<T> {
|
||||
let id = Id::new(k);
|
||||
self.egui.data(|d| d.get_temp(id))
|
||||
}
|
||||
|
||||
/// Simple wrapper around egui temp data
|
||||
pub fn set<T: Clone + Send + Sync + 'static>(&mut self, k: &str, v: T) {
|
||||
self.egui.data_mut(|d| d.insert_temp(Id::new(k), v));
|
||||
}
|
||||
}
|
||||
|
||||
const BLACK_PIXEL: [u8; 4] = [0, 0, 0, 0];
|
||||
pub fn image_from_cache<'a>(img_cache: &mut ImageCache, ctx: &Context, url: &str) -> Image<'a> {
|
||||
if let Some(promise) = img_cache.map().get(url) {
|
||||
|
@ -1,10 +1,12 @@
|
||||
use egui::{Color32, Margin};
|
||||
|
||||
pub const FONT_SIZE: f32 = 13.0;
|
||||
pub const FONT_SIZE_LG: f32 = FONT_SIZE * 1.5;
|
||||
pub const ROUNDING_DEFAULT: f32 = 12.0;
|
||||
pub const MARGIN_DEFAULT: Margin = Margin::symmetric(12., 6.);
|
||||
pub const PRIMARY: Color32 = Color32::from_rgb(248, 56, 217);
|
||||
pub const NEUTRAL_500: Color32 = Color32::from_rgb(115, 115, 115);
|
||||
pub const NEUTRAL_700: Color32 = Color32::from_rgb(64, 64, 64);
|
||||
pub const NEUTRAL_800: Color32 = Color32::from_rgb(38, 38, 38);
|
||||
pub const NEUTRAL_900: Color32 = Color32::from_rgb(23, 23, 23);
|
||||
pub const ZAP: Color32 = Color32::from_rgb(255, 141, 43);
|
||||
|
@ -50,7 +50,7 @@ impl Avatar {
|
||||
response
|
||||
}
|
||||
|
||||
pub fn render(&self, ui: &mut Ui, img_cache: &mut ImageCache) -> Response {
|
||||
pub fn render(self, ui: &mut Ui, img_cache: &mut ImageCache) -> Response {
|
||||
let size_v = self.size.unwrap_or(40.);
|
||||
let size = Vec2::new(size_v, size_v);
|
||||
if !ui.is_visible() {
|
||||
|
@ -1,13 +1,35 @@
|
||||
use crate::theme::{NEUTRAL_800, ROUNDING_DEFAULT};
|
||||
use egui::{Color32, CursorIcon, Frame, Margin, Response, Sense, Ui};
|
||||
use crate::theme::{MARGIN_DEFAULT, NEUTRAL_800, ROUNDING_DEFAULT};
|
||||
use egui::{Color32, CursorIcon, Frame, Response, Sense, Ui, WidgetText};
|
||||
|
||||
pub struct Button {
|
||||
color: Color32,
|
||||
disabled: bool,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
pub fn new() -> Self {
|
||||
Self { color: NEUTRAL_800 }
|
||||
Self {
|
||||
color: NEUTRAL_800,
|
||||
disabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_color(mut self, color: impl Into<Color32>) -> Self {
|
||||
self.color = color.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disabled(mut self, v: bool) -> Self {
|
||||
self.disabled = v;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn simple(ui: &mut Ui, content: &str) -> Response {
|
||||
Button::new().show(ui, |ui| ui.label(content))
|
||||
}
|
||||
|
||||
pub fn text(self, ui: &mut Ui, text: impl Into<WidgetText>) -> Response {
|
||||
self.show(ui, |ui| ui.label(text))
|
||||
}
|
||||
|
||||
pub fn show<F>(self, ui: &mut Ui, add_contents: F) -> Response
|
||||
@ -15,15 +37,20 @@ impl Button {
|
||||
F: FnOnce(&mut Ui) -> Response,
|
||||
{
|
||||
let r = Frame::none()
|
||||
.inner_margin(Margin::symmetric(12., 8.))
|
||||
.inner_margin(MARGIN_DEFAULT)
|
||||
.fill(self.color)
|
||||
.rounding(ROUNDING_DEFAULT)
|
||||
.multiply_with_opacity(if self.disabled { 0.5 } else { 1.0 })
|
||||
.show(ui, add_contents);
|
||||
|
||||
let id = r.response.id;
|
||||
ui.interact(
|
||||
r.response
|
||||
.on_hover_and_drag_cursor(CursorIcon::PointingHand)
|
||||
.on_hover_and_drag_cursor(if self.disabled {
|
||||
CursorIcon::NotAllowed
|
||||
} else {
|
||||
CursorIcon::PointingHand
|
||||
})
|
||||
.rect,
|
||||
id,
|
||||
Sense::click(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::theme::{MARGIN_DEFAULT, ROUNDING_DEFAULT, ZAP};
|
||||
use crate::widgets::Avatar;
|
||||
use crate::zap::Zap;
|
||||
use crate::zap::{format_sats, Zap};
|
||||
use eframe::emath::Align;
|
||||
use eframe::epaint::text::{LayoutJob, TextFormat, TextWrapMode};
|
||||
use eframe::epaint::Color32;
|
||||
@ -43,7 +43,7 @@ impl<'a> ChatZap<'a> {
|
||||
job.append("zapped", 5.0, format.clone());
|
||||
format.color = ZAP;
|
||||
job.append(
|
||||
(self.zap.amount / 1000).to_string().as_str(),
|
||||
&format_sats((self.zap.amount / 1000) as f32),
|
||||
5.0,
|
||||
format.clone(),
|
||||
);
|
||||
|
@ -13,6 +13,7 @@ mod stream_title;
|
||||
mod text_input;
|
||||
mod username;
|
||||
mod write_chat;
|
||||
mod zap;
|
||||
|
||||
use crate::note_ref::NoteRef;
|
||||
use crate::route::RouteServices;
|
||||
|
@ -1,8 +1,10 @@
|
||||
use crate::note_util::NoteUtil;
|
||||
use crate::route::RouteServices;
|
||||
use crate::stream_info::StreamInfo;
|
||||
use crate::theme::MARGIN_DEFAULT;
|
||||
use crate::widgets::zap::ZapButton;
|
||||
use crate::widgets::Profile;
|
||||
use egui::{Color32, Frame, Label, Margin, Response, RichText, TextWrapMode, Ui};
|
||||
use egui::{Color32, Frame, Label, Response, RichText, TextWrapMode, Ui};
|
||||
use nostrdb::Note;
|
||||
|
||||
pub struct StreamTitle<'a> {
|
||||
@ -15,17 +17,19 @@ impl<'a> StreamTitle<'a> {
|
||||
}
|
||||
pub fn render(&mut self, ui: &mut Ui, services: &mut RouteServices<'_, '_>) -> Response {
|
||||
Frame::none()
|
||||
.outer_margin(Margin::symmetric(12., 8.))
|
||||
.outer_margin(MARGIN_DEFAULT)
|
||||
.show(ui, |ui| {
|
||||
ui.style_mut().spacing.item_spacing.y = 8.;
|
||||
let title = RichText::new(self.event.title().unwrap_or("Untitled"))
|
||||
.size(20.)
|
||||
.color(Color32::WHITE);
|
||||
ui.add(Label::new(title.strong()).wrap_mode(TextWrapMode::Truncate));
|
||||
ui.add(Label::new(title.strong()).wrap_mode(TextWrapMode::Wrap));
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
Profile::new(self.event.host())
|
||||
.size(32.)
|
||||
.render(ui, services);
|
||||
ZapButton::event(self.event).render(ui, services);
|
||||
});
|
||||
|
||||
if let Some(summary) = self
|
||||
.event
|
||||
@ -34,7 +38,7 @@ impl<'a> StreamTitle<'a> {
|
||||
{
|
||||
if !summary.is_empty() {
|
||||
let summary = RichText::new(summary).color(Color32::WHITE);
|
||||
ui.add(Label::new(summary).wrap_mode(TextWrapMode::Truncate));
|
||||
ui.add(Label::new(summary).wrap_mode(TextWrapMode::Wrap));
|
||||
}
|
||||
}
|
||||
})
|
||||
|
286
src/widgets/zap.rs
Normal file
286
src/widgets/zap.rs
Normal file
@ -0,0 +1,286 @@
|
||||
use crate::link::NostrLink;
|
||||
use crate::route::RouteServices;
|
||||
use crate::stream_info::StreamInfo;
|
||||
use crate::theme::{
|
||||
FONT_SIZE_LG, MARGIN_DEFAULT, NEUTRAL_700, NEUTRAL_800, NEUTRAL_900, PRIMARY, ROUNDING_DEFAULT,
|
||||
};
|
||||
use crate::widgets::{Button, NativeTextInput};
|
||||
use crate::zap::format_sats;
|
||||
use anyhow::{anyhow, bail};
|
||||
use egui::{vec2, Frame, Grid, Response, RichText, Stroke, Ui, Widget};
|
||||
use egui_modal::Modal;
|
||||
use egui_qr::QrCodeWidget;
|
||||
use enostr::PoolRelay;
|
||||
use itertools::Itertools;
|
||||
use lnurl::pay::{LnURLPayInvoice, PayResponse};
|
||||
use nostr::prelude::{hex, ZapRequestData};
|
||||
use nostr::{serde_json, EventBuilder, JsonUtil, Kind, PublicKey, Tag, Url};
|
||||
use nostrdb::Note;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::task::Poll;
|
||||
|
||||
pub enum ZapTarget<'a> {
|
||||
PublicKey { pubkey: [u8; 32] },
|
||||
Event { event: &'a Note<'a> },
|
||||
}
|
||||
|
||||
impl Display for ZapTarget<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ZapTarget::PublicKey { pubkey } => write!(f, "{}", hex::encode(pubkey)),
|
||||
ZapTarget::Event { event } => write!(f, "{}", hex::encode(event.id())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ZapState {
|
||||
NotStarted,
|
||||
Ready { service: PayResponse },
|
||||
FetchingInvoice { callback: String },
|
||||
Invoice { invoice: LnURLPayInvoice },
|
||||
Error(String),
|
||||
}
|
||||
|
||||
pub struct ZapButton<'a> {
|
||||
target: ZapTarget<'a>,
|
||||
}
|
||||
|
||||
impl<'a> ZapButton<'a> {
|
||||
pub fn pubkey(pubkey: [u8; 32]) -> Self {
|
||||
Self {
|
||||
target: ZapTarget::PublicKey { pubkey },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(event: &'a Note<'a>) -> Self {
|
||||
Self {
|
||||
target: ZapTarget::Event { event },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self, ui: &mut Ui, services: &mut RouteServices) -> Response {
|
||||
// TODO: fix id
|
||||
let modal = Modal::new(ui.ctx(), format!("zapper-{}", 0)).with_close_on_outside_click(true);
|
||||
|
||||
let resp = Button::new().show(ui, |ui| ui.label("ZAP"));
|
||||
if resp.clicked() {
|
||||
modal.open();
|
||||
}
|
||||
ui.visuals_mut().window_rounding = ROUNDING_DEFAULT.into();
|
||||
ui.visuals_mut().window_stroke = Stroke::NONE;
|
||||
ui.visuals_mut().window_fill = NEUTRAL_900;
|
||||
|
||||
modal.show(|ui| {
|
||||
Frame::none().inner_margin(MARGIN_DEFAULT).show(ui, |ui| {
|
||||
ui.spacing_mut().item_spacing = vec2(8.0, 8.0);
|
||||
|
||||
let pubkey = match &self.target {
|
||||
ZapTarget::PublicKey { pubkey } => pubkey,
|
||||
ZapTarget::Event { event } => event.host(),
|
||||
};
|
||||
|
||||
// zapping state machine
|
||||
let zap_state = services.get("zap_state").unwrap_or(ZapState::NotStarted);
|
||||
match &zap_state {
|
||||
ZapState::NotStarted => match services.fetch_lnurlp(pubkey) {
|
||||
Ok(Poll::Ready(r)) => {
|
||||
services.set("zap_state", ZapState::Ready { service: r })
|
||||
}
|
||||
Err(e) => services.set("zap_state", ZapState::Error(e.to_string())),
|
||||
_ => {}
|
||||
},
|
||||
ZapState::FetchingInvoice { callback } => {
|
||||
match self.zap_get_invoice(callback, services) {
|
||||
Ok(Poll::Ready(s)) => {
|
||||
services.set("zap_state", ZapState::Invoice { invoice: s })
|
||||
}
|
||||
Err(e) => services.set("zap_state", ZapState::Error(e.to_string())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// when ready state, show zap button
|
||||
match &zap_state {
|
||||
ZapState::Ready { service } => {
|
||||
self.render_input(ui, services, pubkey, service);
|
||||
}
|
||||
ZapState::Invoice { invoice } => {
|
||||
if let Ok(q) = QrCodeWidget::from_data(invoice.pr.as_bytes()) {
|
||||
ui.add_sized(vec2(256., 256.), q);
|
||||
|
||||
let rt = RichText::new(&invoice.pr).code();
|
||||
ui.label(rt);
|
||||
}
|
||||
}
|
||||
ZapState::Error(e) => {
|
||||
ui.label(e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if modal.was_outside_clicked() {
|
||||
services.set("zap_state", ZapState::NotStarted)
|
||||
}
|
||||
resp
|
||||
}
|
||||
|
||||
fn zap_get_invoice(
|
||||
&self,
|
||||
callback: &str,
|
||||
services: &mut RouteServices,
|
||||
) -> anyhow::Result<Poll<LnURLPayInvoice>> {
|
||||
match services.fetch(callback) {
|
||||
Poll::Ready(Ok(r)) => {
|
||||
if r.ok {
|
||||
let inv: LnURLPayInvoice = serde_json::from_slice(&r.bytes)?;
|
||||
Ok(Poll::Ready(inv))
|
||||
} else {
|
||||
bail!("Invalid response code {}", r.status);
|
||||
}
|
||||
}
|
||||
Poll::Ready(Err(e)) => Err(anyhow!("{}", e)),
|
||||
Poll::Pending => Ok(Poll::Pending),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_input(
|
||||
&self,
|
||||
ui: &mut Ui,
|
||||
services: &mut RouteServices,
|
||||
pubkey: &[u8; 32],
|
||||
service: &PayResponse,
|
||||
) {
|
||||
let target_name = match self.target {
|
||||
ZapTarget::PublicKey { pubkey } => services.profile(&pubkey).and_then(|p| p.name()),
|
||||
ZapTarget::Event { event } => {
|
||||
let host = event.host();
|
||||
services.profile(host).and_then(|p| p.name())
|
||||
}
|
||||
};
|
||||
let fallback_name = self.target.to_string();
|
||||
let target_name = target_name.unwrap_or(&fallback_name);
|
||||
ui.label(RichText::new(format!("Zap {}", target_name)).size(FONT_SIZE_LG));
|
||||
|
||||
ui.label("Zap amount in sats");
|
||||
|
||||
// amount buttons
|
||||
const SATS_AMOUNTS: &[u64] = &[
|
||||
21, 69, 121, 420, 1_000, 2_100, 4_200, 10_000, 21_000, 42_000, 69_000, 100_000,
|
||||
210_000, 500_000, 1_000_000,
|
||||
];
|
||||
const COLS: u32 = 5;
|
||||
let selected_amount = services.get("zap_amount").unwrap_or(0);
|
||||
Grid::new("zap_amounts_grid").show(ui, |ui| {
|
||||
let mut ctr = 0;
|
||||
for x in SATS_AMOUNTS {
|
||||
if Button::new()
|
||||
.with_color(if selected_amount == *x {
|
||||
NEUTRAL_700
|
||||
} else {
|
||||
NEUTRAL_800
|
||||
})
|
||||
.text(ui, &format_sats(*x as f32))
|
||||
.clicked()
|
||||
{
|
||||
services.set("zap_amount", *x);
|
||||
}
|
||||
ctr += 1;
|
||||
if ctr % COLS == 0 {
|
||||
ui.end_row();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// comment section
|
||||
let mut zap_comment = services.get("zap_comment").unwrap_or(String::new());
|
||||
ui.label(format!("Your comment for {}", target_name));
|
||||
let old_len = zap_comment.len();
|
||||
NativeTextInput::new(&mut zap_comment)
|
||||
.with_frame(true)
|
||||
.ui(ui);
|
||||
|
||||
if Button::new().with_color(PRIMARY).text(ui, "Zap!").clicked() {
|
||||
// on-click setup callback URL and transition state
|
||||
match self.zap_callback(
|
||||
services,
|
||||
pubkey,
|
||||
&zap_comment,
|
||||
selected_amount * 1_000,
|
||||
&service,
|
||||
) {
|
||||
Ok(callback) => services.set(
|
||||
"zap_state",
|
||||
ZapState::FetchingInvoice {
|
||||
callback: callback.to_string(),
|
||||
},
|
||||
),
|
||||
Err(e) => services.set("zap_state", ZapState::Error(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
if zap_comment.len() != old_len {
|
||||
services.set("zap_comment", zap_comment);
|
||||
}
|
||||
}
|
||||
|
||||
fn zap_callback(
|
||||
&self,
|
||||
services: &mut RouteServices,
|
||||
pubkey: &[u8; 32],
|
||||
zap_comment: &str,
|
||||
amount: u64,
|
||||
lnurlp: &PayResponse,
|
||||
) -> anyhow::Result<Url> {
|
||||
let relays: Vec<Url> = services
|
||||
.ctx
|
||||
.pool
|
||||
.relays
|
||||
.iter()
|
||||
.filter_map(|r| match r {
|
||||
PoolRelay::Websocket(w) => Url::parse(&w.relay.url).ok(),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
if relays.is_empty() {
|
||||
bail!("No relays found");
|
||||
}
|
||||
let mut req = ZapRequestData::new(PublicKey::from_slice(pubkey)?, relays)
|
||||
.message(zap_comment)
|
||||
.amount(amount);
|
||||
match &self.target {
|
||||
ZapTarget::Event { event } => {
|
||||
req.event_coordinate = Some(
|
||||
NostrLink::from_note(event)
|
||||
.try_into()
|
||||
.map_err(|e| anyhow!("{:?}", e))?,
|
||||
)
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let req_tags: Vec<Tag> = req.into();
|
||||
let keys = if let Some(k) = services.current_account_keys() {
|
||||
k
|
||||
} else {
|
||||
bail!("Not logged in")
|
||||
};
|
||||
|
||||
let req_ev = EventBuilder::new(Kind::ZapRequest, zap_comment)
|
||||
.tags(req_tags)
|
||||
.sign_with_keys(&keys)?;
|
||||
|
||||
let mut url = Url::parse(&lnurlp.callback)?;
|
||||
url.query_pairs_mut()
|
||||
.append_pair("amount", amount.to_string().as_str());
|
||||
if lnurlp.nostr_pubkey.is_some() {
|
||||
url.query_pairs_mut()
|
||||
.append_pair("nostr", req_ev.as_json().as_str());
|
||||
}
|
||||
Ok(url)
|
||||
}
|
||||
}
|
17
src/zap.rs
17
src/zap.rs
@ -1,5 +1,8 @@
|
||||
use crate::note_util::NoteUtil;
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use fixed_decimal::FixedDecimal;
|
||||
use icu::decimal::FixedDecimalFormatter;
|
||||
use icu::locid::Locale;
|
||||
use nostr::{Event, JsonUtil, Kind, TagStandard};
|
||||
use nostrdb::Note;
|
||||
|
||||
@ -54,3 +57,17 @@ impl<'a> Zap<'a> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format_sats(n: f32) -> String {
|
||||
let (div_n, suffix) = if n >= 1_000. && n < 1_000_000. {
|
||||
(1_000., "K")
|
||||
} else if n >= 1_000_000. {
|
||||
(1_000_000., "M")
|
||||
} else {
|
||||
(1., "")
|
||||
};
|
||||
|
||||
let fmt = FixedDecimalFormatter::try_new(&Locale::UND.into(), Default::default()).expect("icu");
|
||||
let d: FixedDecimal = (n / div_n).to_string().parse().expect("fixed decimal");
|
||||
format!("{}{}", fmt.format_to_string(&d), suffix)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user