atagen

macltok

public
Star0
Watch0

Commit 9eabb6b7587d

tarball

atagen <boss@atagen.co>2026-10-25 07:40 UTC

8 files changed+258-72
timeline: preserve pack fixes with segmented history
Modified file: Cargo.lock+10 additions−10 deletions
@@ -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",
Modified file: Cargo.toml+9 additions−9 deletions
@@ -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 needs
5454 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
Modified file: docs/segmented-history.md+67 additions−33 deletions
@@ -1,11 +1,13 @@
11 # Segmented history implementation
22
33 The durable implementation is in the macltok `segmented-history` feature
4-worktree. Its SDK dependency has been published at
5-`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 has
8-not yet been pinned to the macltok revision.
4+worktree. The combined SDK revision
5+`9b2c17fb10703d90b177b02588912a34cecfa11f` is published on its
6+`segmented-history` bookmark. It includes the reaction compatibility fix from
7+`c0a` and segmented history from `49a9`. The macltok history branch includes
8+pack fixes from `e4bb841a` and the `macltok-pack-fixes` worktree through
9+`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 behavior
1113
@@ -60,17 +62,19 @@ through this update path.
6062
6163 ## Public and wire shapes
6264
63-`HistoryRepairCommand::FillGap { room_id, gap_id }` is the supported targeted
64-fill operation, sent through `ClientHandle::execute` as
65-`Command::HistoryRepair`. It returns `CommandOutput::Done` on success. The older
66-policy and staged repair command variants remain available for serde
67-compatibility, but room opening no longer consults the saved policy. `Continue`
68-is invalid because continuation must identify its gap. `GetPolicy` and
69-`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 while
73-the fill runs. Recent loading has its own room keyed `RecentHistoryState` with
65+`ClientHandle::fill_history_gap(room_id, gap_id)` is the convenience method for
66+the targeted fill. It returns `Result<(), Error>`. The underlying command is
67+`HistoryRepairCommand::FillGap { room_id, gap_id }`, sent as
68+`Command::HistoryRepair`. The older policy and staged repair command variants
69+remain available for serde compatibility, but room opening no longer consults
70+the saved policy. `Continue` is invalid because continuation must identify its
71+gap. `GetPolicy` and `SetPolicy` remain compatibility operations and do not
72+control opening.
73+
74+The inline gap lifecycle value `Fetching` is reserved and is not currently
75+emitted by timeline projection. During a targeted fill, loading is signaled by
76+a `HistoryRepairReport` with `gap_id` set and `phase` set to `Bridging`. Recent
77+loading has its own room keyed `RecentHistoryState` with
7478 `Idle`, `Loading`, `Loaded`, or `Failed` values. The mirror exposes that state in
7579 `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 ```json
9195 {
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+```json
108+{
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": null
116+ }
117+}
118+```
119+
120+The mirror snapshot stores recent loading separately by room ID. This fragment
121+shows the serialized `State.recent_history` field.
122+
123+```json
124+{
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 report
101133 before acknowledging it, so command completion follows publication.
102134
103-## Remaining publication and Malice work
135+## Malice integration
104136
105-Publish the SDK revision on the user's fork, then publish the macltok revision
106-that pins it. Update the Malice macltok dependency in both native and worker
107-manifests and regenerate the applicable lockfiles against that same revision.
108-The current worktree has not recorded a final publishable commit or revision
109-for these changes.
137+Pin the published macltok `segmented-history` revision in Malice's native and
138+worker manifests and regenerate their lockfiles. The SDK dependency is already
139+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 and
155-the web worker runtime. `nix flake check` is the aggregate gate and remains
156-pending.
185+the web worker runtime. The default native gate passed all 86 tests. The
186+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 with
189+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 timeline
160-checks pass with 123 unit tests, seven room unit tests, and 14 timed integration
161-tests. Two headless Firefox tests pass for the IndexedDB shared gap storage
162-contract with plaintext and encrypted stores. The full flake gate remains
163-pending. Complete worker restart and encrypted room recovery have not been
164-tested.
192+The SDK WebAssembly compile passes with IndexedDB enabled. SDK UI timeline
193+checks pass with 123 unit tests. Separate SDK room checks pass with seven unit
194+tests and 14 timed integration tests. Two headless Firefox tests pass for the
195+IndexedDB shared gap storage contract with plaintext and encrypted stores. The
196+full SQLite and mirror gate, aggregate WebAssembly gate, and browser worker
197+runtime gate remain unverified. Complete worker restart and encrypted room
198+recovery remain integration acceptance checks.
Modified file: harness/web/Cargo.lock+8 additions−8 deletions
@@ -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",
Modified file: src/engine/packs.rs+108 additions−9 deletions
@@ -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()).await
227229 }
@@ -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()).await
237243 }
@@ -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+ .await
260+ }
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)) => images
266285 .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+ image
515+}
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+ values
526+ .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 image
542+ .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))
Modified file: src/engine/repair.rs+16 additions−3 deletions
@@ -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 self
200+ .context
201+ .events
202+ .send(crate::Event::HistoryRepairChanged(progress))
203+ .await
204+ .is_err()
205+ {
193206 responder.done(Err(Error::interrupted()));
194207 return;
195208 }
Modified file: src/model.rs+3 additions
@@ -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 be
466468 /// 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
Modified file: src/transport.rs+37 additions
@@ -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+ .await
1114+ }
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, _) = self
11131124 .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+ .await
1146+ }
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, _) = self
11331157 .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+ .await
1179+ }
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) = self
11531189 .run_clone(PackCommand::ClonePack {
11541190 from_pack,
11551191 to,
11561192 display_name,
1193+ usage,
11571194 })
11581195 .await?;
11591196 Ok(skipped)