Commit 9eabb6b7587d
tarball8 files changed+258-72
@@ -2002,7 +2002,7 @@ dependencies = [
20022002
[[package]]20032003
name = "matrix-sdk"20042004
version = "0.18.0"2005
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2005
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"20062006
dependencies = [20072007
"anymap2",20082008
"aquamarine",@@ -2065,7 +2065,7 @@ dependencies = [
20652065
[[package]]20662066
name = "matrix-sdk-base"20672067
version = "0.18.0"2068
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2068
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"20692069
dependencies = [20702070
"as_variant",20712071
"assert_matches",@@ -2095,7 +2095,7 @@ dependencies = [
20952095
[[package]]20962096
name = "matrix-sdk-common"20972097
version = "0.18.0"2098
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2098
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"20992099
dependencies = [21002100
"eyeball-im",21012101
"futures-core",@@ -2118,7 +2118,7 @@ dependencies = [
21182118
[[package]]21192119
name = "matrix-sdk-crypto"21202120
version = "0.18.0"2121
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2121
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"21222122
dependencies = [21232123
"aes",21242124
"aquamarine",@@ -2159,7 +2159,7 @@ dependencies = [
21592159
[[package]]21602160
name = "matrix-sdk-indexeddb"21612161
version = "0.18.0"2162
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2162
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"21632163
dependencies = [21642164
"async-trait",21652165
"base64",@@ -2191,7 +2191,7 @@ dependencies = [
21912191
[[package]]21922192
name = "matrix-sdk-sqlite"21932193
version = "0.18.0"2194
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2194
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"21952195
dependencies = [21962196
"as_variant",21972197
"async-trait",@@ -2218,7 +2218,7 @@ dependencies = [
22182218
[[package]]22192219
name = "matrix-sdk-store-encryption"22202220
version = "0.18.0"2221
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2221
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"22222222
dependencies = [22232223
"base64",22242224
"blake3",@@ -2239,7 +2239,7 @@ dependencies = [
22392239
[[package]]22402240
name = "matrix-sdk-test"22412241
version = "0.18.0"2242
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2242
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"22432243
dependencies = [22442244
"as_variant",22452245
"ctor",@@ -2262,7 +2262,7 @@ dependencies = [
22622262
[[package]]22632263
name = "matrix-sdk-test-macros"22642264
version = "0.18.0"2265
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2265
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"22662266
dependencies = [22672267
"quote",22682268
"syn 2.0.119",@@ -2271,7 +2271,7 @@ dependencies = [
22712271
[[package]]22722272
name = "matrix-sdk-ui"22732273
version = "0.18.0"2274
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2274
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"22752275
dependencies = [22762276
"as_variant",22772277
"async-rx",@@ -26,11 +26,11 @@ indexeddb = [
2626
futures-util = { version = "0.3.31", default-features = false, features = ["std"] }2727
http = "1"2828
imbl = { version = "6.1.0", default-features = false }29
-matrix-sdk = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", default-features = false, features = ["e2e-encryption"] }30
-matrix-sdk-base = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", default-features = false }31
-matrix-sdk-crypto = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", default-features = false }32
-matrix-sdk-store-encryption = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", optional = true }33
-matrix-sdk-ui = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", default-features = false }29
+matrix-sdk = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", default-features = false, features = ["e2e-encryption"] }30
+matrix-sdk-base = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", default-features = false }31
+matrix-sdk-crypto = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", default-features = false }32
+matrix-sdk-store-encryption = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", optional = true }33
+matrix-sdk-ui = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", default-features = false }3434
ruma = { version = "0.16.0", default-features = false, features = ["unstable-msc2545", "unstable-msc4095"] }3535
serde = { version = "1.0.219", features = ["derive", "rc"], optional = true }3636
serde_json = "1.0.143"@@ -48,8 +48,8 @@ getrandom3 = { package = "getrandom", version = "0.3", features = ["wasm_js"] }
4848
getrandom4 = { package = "getrandom", version = "0.4", features = ["wasm_js"] }4949
indexed_db_futures = { package = "matrix_indexed_db_futures", version = "0.7.0", optional = true, features = ["typed-arrays"] }5050
js-sys = { version = "0.3.105", optional = true }51
-matrix-sdk = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", default-features = false, features = ["js"] }52
-matrix-sdk-ui = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", default-features = false, features = ["js"] }51
+matrix-sdk = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", default-features = false, features = ["js"] }52
+matrix-sdk-ui = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", default-features = false, features = ["js"] }5353
# the sdk's own wasm reqwest leaves out stream, which the bounded media download needs5454
reqwest = { version = "0.13.1", default-features = false, features = ["stream"] }5555
uuid = { version = "1", features = ["js"] }@@ -64,8 +64,8 @@ web-sys = { version = "0.3.105", optional = true, features = [
6464
] }6565
6666
[target.'cfg(not(target_family = "wasm"))'.dev-dependencies]67
-matrix-sdk = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e", default-features = false, features = ["testing"] }68
-matrix-sdk-test = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "49a9cd1aff19df1de7e46710cb6c984a1bddc30e" }67
+matrix-sdk = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f", default-features = false, features = ["testing"] }68
+matrix-sdk-test = { git = "ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk", rev = "9b2c17fb10703d90b177b02588912a34cecfa11f" }6969
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread", "signal", "time"] }7070
wiremock = "0.6.5"7171
@@ -1,11 +1,13 @@
11
# Segmented history implementation22
33
The durable implementation is in the macltok `segmented-history` feature4
-worktree. Its SDK dependency has been published at5
-`49a9cd1aff19df1de7e46710cb6c984a1bddc30e` on the SDK `segmented-history`6
-bookmark. The macltok manifest and lockfile are being updated to that revision.7
-The macltok feature bookmark will also be named `segmented-history`. Malice has8
-not yet been pinned to the macltok revision.4
+worktree. The combined SDK revision5
+`9b2c17fb10703d90b177b02588912a34cecfa11f` is published on its6
+`segmented-history` bookmark. It includes the reaction compatibility fix from7
+`c0a` and segmented history from `49a9`. The macltok history branch includes8
+pack fixes from `e4bb841a` and the `macltok-pack-fixes` worktree through9
+`d1313f47`. All nine SDK pins and both lockfiles use the combined SDK revision.10
+The unified macltok bookmark is named `segmented-history`.911
1012
## Current behavior1113
@@ -60,17 +62,19 @@ through this update path.
6062
6163
## Public and wire shapes6264
63
-`HistoryRepairCommand::FillGap { room_id, gap_id }` is the supported targeted64
-fill operation, sent through `ClientHandle::execute` as65
-`Command::HistoryRepair`. It returns `CommandOutput::Done` on success. The older66
-policy and staged repair command variants remain available for serde67
-compatibility, but room opening no longer consults the saved policy. `Continue`68
-is invalid because continuation must identify its gap. `GetPolicy` and69
-`SetPolicy` remain compatibility operations and do not control opening.70
-71
-Targeted fill progress is published as a `HistoryRepairReport` with `gap_id`72
-set and `phase` set to `Bridging`. This report supplies the loading state while73
-the fill runs. Recent loading has its own room keyed `RecentHistoryState` with65
+`ClientHandle::fill_history_gap(room_id, gap_id)` is the convenience method for66
+the targeted fill. It returns `Result<(), Error>`. The underlying command is67
+`HistoryRepairCommand::FillGap { room_id, gap_id }`, sent as68
+`Command::HistoryRepair`. The older policy and staged repair command variants69
+remain available for serde compatibility, but room opening no longer consults70
+the saved policy. `Continue` is invalid because continuation must identify its71
+gap. `GetPolicy` and `SetPolicy` remain compatibility operations and do not72
+control opening.73
+74
+The inline gap lifecycle value `Fetching` is reserved and is not currently75
+emitted by timeline projection. During a targeted fill, loading is signaled by76
+a `HistoryRepairReport` with `gap_id` set and `phase` set to `Bridging`. Recent77
+loading has its own room keyed `RecentHistoryState` with7478
`Idle`, `Loading`, `Loaded`, or `Failed` values. The mirror exposes that state in7579
`recent_history`, separately from gap continuity and repair progress.7680
@@ -89,9 +93,37 @@ follows, with the outer `Command` adding its `HistoryRepair` tag.
8993
9094
```json9195
{92
- "FillGap": {93
- "room_id": "!room:example.org",94
- "gap_id": "opaque-gap-transaction-id"96
+ "HistoryRepair": {97
+ "FillGap": {98
+ "room_id": "!room:example.org",99
+ "gap_id": "opaque-gap-transaction-id"100
+ }101
+ }102
+}103
+```104
+105
+A gap row in `TimelineState.items` has the following shape.106
+107
+```json108
+{109
+ "HistoryGap": {110
+ "id": "opaque-gap-transaction-id",111
+ "older_boundary": "$older:example.org",112
+ "newer_boundary": "$newer:example.org",113
+ "lifecycle": "Idle",114
+ "continuity": "Unresolved",115
+ "failure": null116
+ }117
+}118
+```119
+120
+The mirror snapshot stores recent loading separately by room ID. This fragment121
+shows the serialized `State.recent_history` field.122
+123
+```json124
+{125
+ "recent_history": {126
+ "!room:example.org": "Loading"95127
}96128
}97129
```@@ -100,13 +132,11 @@ The final history repair publication acknowledgement is internal, skipped by
100132
serde, and not part of application wire data. The mirror applies the report101133
before acknowledging it, so command completion follows publication.102134
103
-## Remaining publication and Malice work135
+## Malice integration104136
105
-Publish the SDK revision on the user's fork, then publish the macltok revision106
-that pins it. Update the Malice macltok dependency in both native and worker107
-manifests and regenerate the applicable lockfiles against that same revision.108
-The current worktree has not recorded a final publishable commit or revision109
-for these changes.137
+Pin the published macltok `segmented-history` revision in Malice's native and138
+worker manifests and regenerate their lockfiles. The SDK dependency is already139
+pinned by macltok.110140
111141
Malice should remove the saved policy and threshold controls from room opening.112142
Render each inline gap between its neighboring event sections. A fill should@@ -152,13 +182,17 @@ nix develop .#web -c nu harness/web/run.nu
152182
```153183
154184
The flake checks include native and WebAssembly compilation with IndexedDB and155
-the web worker runtime. `nix flake check` is the aggregate gate and remains156
-pending.185
+the web worker runtime. The default native gate passed all 86 tests. The186
+aggregate `nix flake check` run stopped during the SQLite and mirror build.187
+The remaining aggregate gates were waived for this handoff and have not passed.188
+The focused native history repair and engine suites passed all 11 tests with189
+SQLite, mirror, and serde enabled against the published combined SDK revision.157190
158191
Seven focused SDK recent-history tests and seven segmented-history tests pass.159
-The SDK WebAssembly compile passes with IndexedDB enabled. The SDK UI timeline160
-checks pass with 123 unit tests, seven room unit tests, and 14 timed integration161
-tests. Two headless Firefox tests pass for the IndexedDB shared gap storage162
-contract with plaintext and encrypted stores. The full flake gate remains163
-pending. Complete worker restart and encrypted room recovery have not been164
-tested.192
+The SDK WebAssembly compile passes with IndexedDB enabled. SDK UI timeline193
+checks pass with 123 unit tests. Separate SDK room checks pass with seven unit194
+tests and 14 timed integration tests. Two headless Firefox tests pass for the195
+IndexedDB shared gap storage contract with plaintext and encrypted stores. The196
+full SQLite and mirror gate, aggregate WebAssembly gate, and browser worker197
+runtime gate remain unverified. Complete worker restart and encrypted room198
+recovery remain integration acceptance checks.@@ -1907,7 +1907,7 @@ dependencies = [
19071907
[[package]]19081908
name = "matrix-sdk"19091909
version = "0.18.0"1910
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"1910
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"19111911
dependencies = [19121912
"anymap2",19131913
"aquamarine",@@ -1967,7 +1967,7 @@ dependencies = [
19671967
[[package]]19681968
name = "matrix-sdk-base"19691969
version = "0.18.0"1970
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"1970
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"19711971
dependencies = [19721972
"as_variant",19731973
"async-trait",@@ -1993,7 +1993,7 @@ dependencies = [
19931993
[[package]]19941994
name = "matrix-sdk-common"19951995
version = "0.18.0"1996
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"1996
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"19971997
dependencies = [19981998
"eyeball-im",19991999
"futures-core",@@ -2016,7 +2016,7 @@ dependencies = [
20162016
[[package]]20172017
name = "matrix-sdk-crypto"20182018
version = "0.18.0"2019
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2019
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"20202020
dependencies = [20212021
"aes",20222022
"aquamarine",@@ -2056,7 +2056,7 @@ dependencies = [
20562056
[[package]]20572057
name = "matrix-sdk-indexeddb"20582058
version = "0.18.0"2059
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2059
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"20602060
dependencies = [20612061
"async-trait",20622062
"base64",@@ -2088,7 +2088,7 @@ dependencies = [
20882088
[[package]]20892089
name = "matrix-sdk-sqlite"20902090
version = "0.18.0"2091
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2091
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"20922092
dependencies = [20932093
"as_variant",20942094
"async-trait",@@ -2115,7 +2115,7 @@ dependencies = [
21152115
[[package]]21162116
name = "matrix-sdk-store-encryption"21172117
version = "0.18.0"2118
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2118
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"21192119
dependencies = [21202120
"base64",21212121
"blake3",@@ -2136,7 +2136,7 @@ dependencies = [
21362136
[[package]]21372137
name = "matrix-sdk-ui"21382138
version = "0.18.0"2139
-source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=49a9cd1aff19df1de7e46710cb6c984a1bddc30e#49a9cd1aff19df1de7e46710cb6c984a1bddc30e"2139
+source = "git+ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk?rev=9b2c17fb10703d90b177b02588912a34cecfa11f#9b2c17fb10703d90b177b02588912a34cecfa11f"21402140
dependencies = [21412141
"as_variant",21422142
"async-rx",@@ -220,8 +220,10 @@ pub(super) async fn run_detached(
220220
from_pack,221221
shortcode,222222
to,223
+ usage,223224
} => {224
- let image = pack_image(&client, &from_pack, &shortcode).await?;225
+ let mut image = pack_image(&client, &from_pack, &shortcode).await?;226
+ apply_usage(&mut image, usage);225227
let image = rehost_image(&client, image, max_bytes).await?;226228
insert(&mutations, to, vec![(shortcode, image)], None, Vec::new()).await227229
}@@ -230,8 +232,12 @@ pub(super) async fn run_detached(
230232
event_id,231233
to,232234
shortcode,235
+ usage,233236
} => {234
- let image = sticker_image(&client, room_id, event_id).await?;237
+ let mut image = sticker_image(&client, room_id, event_id).await?;238
+ let body = sticker_body(&image, &shortcode);239
+ image.insert("body".to_owned(), body.into());240
+ apply_usage(&mut image, usage);235241
let image = rehost_image(&client, image, max_bytes).await?;236242
insert(&mutations, to, vec![(shortcode, image)], None, Vec::new()).await237243
}@@ -239,7 +245,19 @@ pub(super) async fn run_detached(
239245
from_pack,240246
to,241247
display_name,242
- } => clone_pack(&client, &mutations, max_bytes, &from_pack, to, display_name).await,248
+ usage,249
+ } => {250
+ clone_pack(251
+ &client,252
+ &mutations,253
+ max_bytes,254
+ &from_pack,255
+ to,256
+ display_name,257
+ usage,258
+ )259
+ .await260
+ }243261
_ => Err(Error::new(ErrorKind::Other, "pack command was misrouted")),244262
}245263
}@@ -251,6 +269,7 @@ async fn clone_pack(
251269
from_pack: &str,252270
to: ImagePackSource,253271
display_name: Option<String>,272
+ usage: Option<Vec<PackUsage>>,254273
) -> Result<CommandOutput, Error> {255274
let source = resolve_pack_id(client, from_pack)256275
.ok_or_else(|| Error::new(ErrorKind::Invalid, "no pack has that id"))?;@@ -265,7 +284,8 @@ async fn clone_pack(
265284
Some(JsonValue::Object(images)) => images266285
.iter()267286
.filter_map(|(shortcode, image)| {268
- let image = with_pack_usage(image.as_object()?.clone(), &meta);287
+ let mut image = with_pack_usage(image.as_object()?.clone(), &meta);288
+ apply_usage(&mut image, usage.clone());269289
Some((shortcode.clone(), image))270290
})271291
.collect(),@@ -340,7 +360,7 @@ async fn send_sticker(
340360
.and_then(mxc)341361
.ok_or_else(|| Error::new(ErrorKind::Invalid, "that image has no usable url"))?;342362
let content = json!({343
- "body": image.get("body").and_then(JsonValue::as_str).unwrap_or(shortcode),363
+ "body": sticker_body(&image, shortcode),344364
"url": url,345365
"info": image.get("info").cloned().unwrap_or_else(|| json!({})),346366
});@@ -487,12 +507,43 @@ async fn write_pack(
487507
}488508
489509
fn with_pack_usage(mut image: JsonObject, meta: &JsonObject) -> JsonObject {490
- if !image.contains_key("usage")491
- && let Some(usage) = meta.get("usage")492
- {493
- image.insert("usage".to_owned(), usage.clone());510
+ let usage = known_usage(image.get("usage"))511
+ .or_else(|| known_usage(meta.get("usage")))512
+ .unwrap_or_else(|| vec![PackUsage::Emoticon, PackUsage::Sticker]);513
+ image.insert("usage".to_owned(), usage_json(usage));514
+ image515
+}516
+517
+fn known_usage(value: Option<&JsonValue>) -> Option<Vec<PackUsage>> {518
+ let values = value?.as_array()?;519
+ let usage = [520
+ ("emoticon", PackUsage::Emoticon),521
+ ("sticker", PackUsage::Sticker),522
+ ]523
+ .into_iter()524
+ .filter_map(|(name, usage)| {525
+ values526
+ .iter()527
+ .any(|value| value.as_str() == Some(name))528
+ .then_some(usage)529
+ })530
+ .collect::<Vec<_>>();531
+ (!usage.is_empty()).then_some(usage)532
+}533
+534
+fn apply_usage(image: &mut JsonObject, usage: Option<Vec<PackUsage>>) {535
+ if let Some(usage) = usage {536
+ image.insert("usage".to_owned(), usage_json(usage));494537
}538
+}539
+540
+fn sticker_body(image: &JsonObject, shortcode: &str) -> String {495541
image542
+ .get("body")543
+ .and_then(JsonValue::as_str)544
+ .filter(|body| !body.trim().is_empty())545
+ .unwrap_or(shortcode)546
+ .to_owned()496547
}497548
498549
fn free_shortcode(images: &JsonObject, wanted: &str) -> String {@@ -573,6 +624,54 @@ fn object(value: JsonValue) -> JsonObject {
573624
}574625
}575626
627
+#[cfg(test)]628
+mod tests {629
+ use super::*;630
+631
+ #[test]632
+ fn pack_usage_inherits_known_values_then_defaults() {633
+ let pack = object(json!({ "usage": ["sticker", "unknown"] }));634
+ let inherited = with_pack_usage(object(json!({ "usage": [] })), &pack);635
+ assert_eq!(inherited.get("usage"), Some(&json!(["sticker"])));636
+637
+ let pack = object(json!({ "usage": ["emoticon"] }));638
+ let inherited = with_pack_usage(object(json!({ "usage": ["unknown"] })), &pack);639
+ assert_eq!(inherited.get("usage"), Some(&json!(["emoticon"])));640
+641
+ let pack = object(json!({ "usage": ["sticker"] }));642
+ let inherited = with_pack_usage(object(json!({ "usage": ["emoticon", "unknown"] })), &pack);643
+ assert_eq!(inherited.get("usage"), Some(&json!(["emoticon"])));644
+645
+ let pack = object(json!({ "usage": [] }));646
+ let inherited = with_pack_usage(object(json!({})), &pack);647
+ assert_eq!(648
+ inherited.get("usage"),649
+ Some(&json!(["emoticon", "sticker"]))650
+ );651
+652
+ let pack = object(json!({ "usage": ["unknown"] }));653
+ let inherited = with_pack_usage(object(json!({})), &pack);654
+ assert_eq!(655
+ inherited.get("usage"),656
+ Some(&json!(["emoticon", "sticker"]))657
+ );658
+ }659
+660
+ #[test]661
+ fn sticker_body_falls_back_when_missing_or_blank() {662
+ assert_eq!(sticker_body(&object(json!({})), "wave"), "wave");663
+ assert_eq!(sticker_body(&object(json!({ "body": "" })), "wave"), "wave");664
+ assert_eq!(665
+ sticker_body(&object(json!({ "body": " \t\n " })), "wave"),666
+ "wave"667
+ );668
+ assert_eq!(669
+ sticker_body(&object(json!({ "body": " hello " })), "wave"),670
+ " hello "671
+ );672
+ }673
+}674
+576675
fn parse(json: &str) -> Result<JsonObject, Error> {577676
serde_json::from_str(json)578677
.map_err(|source| Error::with_source(ErrorKind::Server, "the pack is malformed", source))@@ -33,7 +33,11 @@ pub(super) struct Completion {
3333
}3434
3535
impl Completion {36
- pub(super) fn resolve(self, result: Result<CommandOutput, Error>, cancellation: Result<(), Error>) {36
+ pub(super) fn resolve(37
+ self,38
+ result: Result<CommandOutput, Error>,39
+ cancellation: Result<(), Error>,40
+ ) {3741
for responder in self.responders {3842
if responder.cancel_ack {3943
responder.responder.done(cancellation.clone());@@ -137,7 +141,10 @@ impl RepairController {
137141
if let Some(running) = &mut self.running {138142
if let Some(responder) = responder {139143
if running.recent {140
- running.responders.push(PendingResponder { responder, cancel_ack: false });144
+ running.responders.push(PendingResponder {145
+ responder,146
+ cancel_ack: false,147
+ });141148
} else {142149
responder.done(Err(room_busy()));143150
}@@ -189,7 +196,13 @@ impl RepairController {
189196
progress.gap_id = Some(gap_id.clone());190197
progress.strategy = HistoryRepairStrategy::Bridge;191198
progress.phase = HistoryRepairPhase::Bridging;192
- if self.context.events.send(crate::Event::HistoryRepairChanged(progress)).await.is_err() {199
+ if self200
+ .context201
+ .events202
+ .send(crate::Event::HistoryRepairChanged(progress))203
+ .await204
+ .is_err()205
+ {193206
responder.done(Err(Error::interrupted()));194207
return;195208
}@@ -455,12 +455,14 @@ pub enum PackCommand {
455455
from_pack: String,456456
shortcode: String,457457
to: ImagePackSource,458
+ usage: Option<Vec<PackUsage>>,458459
},459460
CloneSticker {460461
room_id: OwnedRoomId,461462
event_id: OwnedEventId,462463
to: ImagePackSource,463464
shortcode: String,465
+ usage: Option<Vec<PackUsage>>,464466
},465467
/// makes the room pack if its state key is free, else merges. images that can't be466468
/// fetched are skipped and listed, the rest are written@@ -468,6 +470,7 @@ pub enum PackCommand {
468470
from_pack: String,469471
to: ImagePackSource,470472
display_name: Option<String>,473
+ usage: Option<Vec<PackUsage>>,471474
},472475
}473476
@@ -1109,11 +1109,23 @@ impl ClientHandle {
11091109
shortcode: String,11101110
to: ImagePackSource,11111111
) -> Result<String, Error> {1112
+ self.clone_pack_image_with_usage(from_pack, shortcode, to, None)1113
+ .await1114
+ }1115
+1116
+ pub async fn clone_pack_image_with_usage(1117
+ &self,1118
+ from_pack: String,1119
+ shortcode: String,1120
+ to: ImagePackSource,1121
+ usage: Option<Vec<PackUsage>>,1122
+ ) -> Result<String, Error> {11121123
let (mut shortcodes, _) = self11131124
.run_clone(PackCommand::CloneImage {11141125
from_pack,11151126
shortcode,11161127
to,1128
+ usage,11171129
})11181130
.await?;11191131
shortcodes@@ -1129,12 +1141,25 @@ impl ClientHandle {
11291141
to: ImagePackSource,11301142
shortcode: String,11311143
) -> Result<String, Error> {1144
+ self.clone_sticker_with_usage(room_id, event_id, to, shortcode, None)1145
+ .await1146
+ }1147
+1148
+ pub async fn clone_sticker_with_usage(1149
+ &self,1150
+ room_id: OwnedRoomId,1151
+ event_id: OwnedEventId,1152
+ to: ImagePackSource,1153
+ shortcode: String,1154
+ usage: Option<Vec<PackUsage>>,1155
+ ) -> Result<String, Error> {11321156
let (mut shortcodes, _) = self11331157
.run_clone(PackCommand::CloneSticker {11341158
room_id,11351159
event_id,11361160
to,11371161
shortcode,1162
+ usage,11381163
})11391164
.await?;11401165
shortcodes@@ -1149,11 +1174,23 @@ impl ClientHandle {
11491174
to: ImagePackSource,11501175
display_name: Option<String>,11511176
) -> Result<Vec<String>, Error> {1177
+ self.clone_pack_with_usage(from_pack, to, display_name, None)1178
+ .await1179
+ }1180
+1181
+ pub async fn clone_pack_with_usage(1182
+ &self,1183
+ from_pack: String,1184
+ to: ImagePackSource,1185
+ display_name: Option<String>,1186
+ usage: Option<Vec<PackUsage>>,1187
+ ) -> Result<Vec<String>, Error> {11521188
let (_, skipped) = self11531189
.run_clone(PackCommand::ClonePack {11541190
from_pack,11551191
to,11561192
display_name,1193
+ usage,11571194
})11581195
.await?;11591196
Ok(skipped)