+1
-1
.gitignore
+1
-1
.gitignore
+137
rust_demo_app/Cargo.lock
+137
rust_demo_app/Cargo.lock
···
94
94
"log",
95
95
"serde",
96
96
"serde-wasm-bindgen 0.6.5",
97
+
"serde_html_form",
97
98
"serde_json",
99
+
"serde_qs",
98
100
"types_demo",
99
101
"wasm-bindgen",
100
102
"wasm-bindgen-futures",
101
103
"wasm-logger",
102
104
"web-sys",
103
105
"yew",
106
+
"yew-router",
107
+
"yewdux",
104
108
]
105
109
106
110
[[package]]
···
516
520
]
517
521
518
522
[[package]]
523
+
name = "darling"
524
+
version = "0.20.11"
525
+
source = "registry+https://github.com/rust-lang/crates.io-index"
526
+
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
527
+
dependencies = [
528
+
"darling_core",
529
+
"darling_macro",
530
+
]
531
+
532
+
[[package]]
533
+
name = "darling_core"
534
+
version = "0.20.11"
535
+
source = "registry+https://github.com/rust-lang/crates.io-index"
536
+
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
537
+
dependencies = [
538
+
"fnv",
539
+
"ident_case",
540
+
"proc-macro2",
541
+
"quote",
542
+
"strsim",
543
+
"syn 2.0.101",
544
+
]
545
+
546
+
[[package]]
547
+
name = "darling_macro"
548
+
version = "0.20.11"
549
+
source = "registry+https://github.com/rust-lang/crates.io-index"
550
+
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
551
+
dependencies = [
552
+
"darling_core",
553
+
"quote",
554
+
"syn 2.0.101",
555
+
]
556
+
557
+
[[package]]
519
558
name = "dashmap"
520
559
version = "6.1.0"
521
560
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1197
1236
source = "registry+https://github.com/rust-lang/crates.io-index"
1198
1237
checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f"
1199
1238
dependencies = [
1239
+
"futures-channel",
1200
1240
"gloo-events 0.2.0",
1201
1241
"js-sys",
1202
1242
"wasm-bindgen",
···
1365
1405
source = "registry+https://github.com/rust-lang/crates.io-index"
1366
1406
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
1367
1407
dependencies = [
1408
+
"futures-channel",
1409
+
"futures-core",
1368
1410
"js-sys",
1369
1411
"wasm-bindgen",
1370
1412
]
···
1815
1857
]
1816
1858
1817
1859
[[package]]
1860
+
name = "ident_case"
1861
+
version = "1.0.1"
1862
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1863
+
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
1864
+
1865
+
[[package]]
1818
1866
name = "idna"
1819
1867
version = "1.0.3"
1820
1868
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2745
2793
]
2746
2794
2747
2795
[[package]]
2796
+
name = "route-recognizer"
2797
+
version = "0.3.1"
2798
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2799
+
checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
2800
+
2801
+
[[package]]
2748
2802
name = "rustc-demangle"
2749
2803
version = "0.1.24"
2750
2804
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3021
3075
]
3022
3076
3023
3077
[[package]]
3078
+
name = "serde_qs"
3079
+
version = "0.12.0"
3080
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3081
+
checksum = "0431a35568651e363364210c91983c1da5eb29404d9f0928b67d4ebcfa7d330c"
3082
+
dependencies = [
3083
+
"percent-encoding",
3084
+
"serde",
3085
+
"thiserror 1.0.69",
3086
+
]
3087
+
3088
+
[[package]]
3024
3089
name = "serde_spanned"
3025
3090
version = "0.6.8"
3026
3091
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3205
3270
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
3206
3271
3207
3272
[[package]]
3273
+
name = "strsim"
3274
+
version = "0.11.1"
3275
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3276
+
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
3277
+
3278
+
[[package]]
3208
3279
name = "subtle"
3209
3280
version = "2.6.1"
3210
3281
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3660
3731
"idna",
3661
3732
"percent-encoding",
3662
3733
]
3734
+
3735
+
[[package]]
3736
+
name = "urlencoding"
3737
+
version = "2.1.3"
3738
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3739
+
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
3663
3740
3664
3741
[[package]]
3665
3742
name = "utf16_iter"
···
4238
4315
"boolinator",
4239
4316
"once_cell",
4240
4317
"prettyplease",
4318
+
"proc-macro-error",
4319
+
"proc-macro2",
4320
+
"quote",
4321
+
"syn 2.0.101",
4322
+
]
4323
+
4324
+
[[package]]
4325
+
name = "yew-router"
4326
+
version = "0.18.0"
4327
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4328
+
checksum = "4ca1d5052c96e6762b4d6209a8aded597758d442e6c479995faf0c7b5538e0c6"
4329
+
dependencies = [
4330
+
"gloo 0.10.0",
4331
+
"js-sys",
4332
+
"route-recognizer",
4333
+
"serde",
4334
+
"serde_urlencoded",
4335
+
"tracing",
4336
+
"urlencoding",
4337
+
"wasm-bindgen",
4338
+
"web-sys",
4339
+
"yew",
4340
+
"yew-router-macro",
4341
+
]
4342
+
4343
+
[[package]]
4344
+
name = "yew-router-macro"
4345
+
version = "0.18.0"
4346
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4347
+
checksum = "42bfd190a07ca8cfde7cd4c52b3ac463803dc07323db8c34daa697e86365978c"
4348
+
dependencies = [
4349
+
"proc-macro2",
4350
+
"quote",
4351
+
"syn 2.0.101",
4352
+
]
4353
+
4354
+
[[package]]
4355
+
name = "yewdux"
4356
+
version = "0.11.0"
4357
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4358
+
checksum = "8030a7de50678c07c038dcb96a42f1e8a7c4cc5610451fbee0c676aa7df42967"
4359
+
dependencies = [
4360
+
"log",
4361
+
"serde",
4362
+
"serde_json",
4363
+
"slab",
4364
+
"thiserror 1.0.69",
4365
+
"wasm-bindgen",
4366
+
"web-sys",
4367
+
"yew",
4368
+
"yewdux-macros",
4369
+
]
4370
+
4371
+
[[package]]
4372
+
name = "yewdux-macros"
4373
+
version = "0.11.0"
4374
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4375
+
checksum = "e7ac6ccd84a49bbce44610d44eb6686a1266337d0cd3aeadb5564ab76a2819f0"
4376
+
dependencies = [
4377
+
"darling",
4241
4378
"proc-macro-error",
4242
4379
"proc-macro2",
4243
4380
"quote",
+64
rust_demo_app/DESIGN.md
+64
rust_demo_app/DESIGN.md
···
1
+
# Rust ATProto Counter Demo - Design Overview
2
+
3
+
This document outlines the design and goals of the Rust ATProto Counter Demo application.
4
+
5
+
## 1. Project Goal
6
+
7
+
The primary goal of this project is to build a simple web application using Rust (compiled to WebAssembly with Yew) that demonstrates integration with the AT Protocol. The core functionality will be a distributed counter.
8
+
9
+
Users will be able to:
10
+
1. Authenticate with their AT Protocol (Bluesky) account via OAuth.
11
+
2. Increment a counter.
12
+
3. When a user increments the counter, the application will publish a unique, singleton record to their personal data repository (PDS). This record signifies their participation in the count.
13
+
4. (Future) The frontend might eventually listen to a firehose stream for these specific records from all users to display a global count.
14
+
15
+
This project serves as a learning exercise and a demonstration of using the `atrium-rs` SDK for AT Protocol interactions in a Rust Wasm context.
16
+
17
+
## 2. Project Structure
18
+
19
+
The `rust_demo_app/` directory contains the core application code, structured as a Cargo workspace:
20
+
21
+
* **`api_demo/`**: A simple Actix-based backend API (currently providing basic counter GET/POST/PUT endpoints). In the context of the ATProto distributed counter, this backend's role might evolve or be less central if the counter state is primarily managed via ATProto records.
22
+
* **`app_demo/`**: The Yew frontend application (Rust compiled to Wasm). This is where the ATProto authentication and record creation logic will reside.
23
+
* **`types_demo/`**: Shared data types between the frontend and backend, including lexicon definitions for any custom ATProto records (e.g., `dev.alternatebuild.rustDemo.count`).
24
+
25
+
## 3. AT Protocol Integration Strategy
26
+
27
+
### 3.1. Authentication
28
+
* The `app_demo` frontend will use the `atrium-oauth` crate to handle OAuth 2.0 authentication against a user's PDS.
29
+
* The flow will be:
30
+
1. User clicks "Login".
31
+
2. App redirects to the PDS authorization endpoint.
32
+
3. User approves.
33
+
4. PDS redirects back to an `/oauth/callback` route in our Yew app.
34
+
5. App exchanges the authorization code for an access token and session data.
35
+
6. Session data (including access and refresh tokens) will be stored securely in the browser's IndexedDB using helper functions and the `atrium_common::store::Store` trait.
36
+
37
+
### 3.2. Distributed Counter Record
38
+
* **Lexicon:** A custom lexicon, `dev.alternatebuild.rustDemo.count`, will be defined.
39
+
* `nsid`: `dev.alternatebuild.rustDemo.count`
40
+
* `key`: `literal:self` (ensuring only one such record per repository, making it a singleton by design for this use case).
41
+
* The record schema itself might be simple, e.g., containing a timestamp or a minimal "counted: true" field. The existence and timestamp of the record are the primary signals.
42
+
* **Action:** When an authenticated user clicks "Increment Counter":
43
+
1. The `app_demo` frontend will use the `atrium-api` (via an authenticated `XrpcClient` or `Agent`) to make a `com.atproto.repo.createRecord` call.
44
+
2. This will create (or update, if `swapCommit` logic is used, though `createRecord` for a new `rkey` each time might be simpler if we only care about the *act* of counting) the `dev.alternatebuild.rustDemo.count` record in their repository. For a true singleton identified by `rkey: "self"`, we'd use `putRecord` to create or update. Given `key: "literal:self"` in the lexicon usually implies the `rkey` will be fixed (often also "self" or a predefined string), `putRecord` is more appropriate for creating/updating a singleton record at a known path. If the lexicon truly means any `rkey` is fine as long as the record name is `self` this implies a collection where only one item has the name self. *Correction*: the `key: "literal:self"` in the lexicon refers to the record's *name* within the collection, not the `rkey` itself. A common pattern for singletons is to use a fixed `rkey` like `"self"`.
45
+
Let's assume we'll use `com.atproto.repo.putRecord` with `rkey: "self"` for this singleton.
46
+
47
+
### 3.3. (Future) Global Count Aggregation
48
+
* To display a global count, the frontend could connect to a Bluesky Appview's firehose subscription endpoint (e.g., `com.atproto.sync.subscribeRepos`).
49
+
* It would filter for `createRecord` or `putRecord` operations related to the `dev.alternatebuild.rustDemo.count` NSID.
50
+
* The frontend would maintain a client-side aggregation of these records to display a live global count. This avoids a centralized counter backend.
51
+
52
+
## 4. Reference Repositories (Informational Only)
53
+
54
+
Within the broader workspace (but **not** part of the `rust_demo_app` build itself), two repositories have been cloned for reference purposes. These are used to understand best practices, see `atrium-rs` usage examples, and borrow code patterns. They are **not** dependencies of `rust_demo_app` and are only present to be read from (e.g., using `rg` - ripgrep) during development.
55
+
56
+
* **`atrium/`**: A clone of the main [`atrium-rs/atrium`](https://github.com/atrium-rs/atrium) repository. This provides access to the source code of the SDK crates themselves (`atrium-api`, `atrium-oauth`, `atrium-common`, etc.) and internal examples or tools like `lexgen`.
57
+
* **`at_2048/`**: A clone of the [`fatfingers23/at_2048`](https://github.com/fatfingers23/at_2048) project. This is a more complete ATProto-enabled web application (a 2048 game) built with Yew and `atrium-rs`. It serves as an excellent practical example for:
58
+
* OAuth flow implementation in Yew.
59
+
* `XrpcClient` and `Agent` usage.
60
+
* Session storage in IndexedDB.
61
+
* Lexicon usage and record manipulation.
62
+
* Yew application structure with ATProto.
63
+
64
+
These repositories are invaluable for accelerating development and ensuring correct usage of the `atrium-rs` ecosystem.
+5
rust_demo_app/app_demo/Cargo.toml
+5
rust_demo_app/app_demo/Cargo.toml
-820
rust_demo_app/app_demo/dist/app_demo-814a61e8321d189a.js
-820
rust_demo_app/app_demo/dist/app_demo-814a61e8321d189a.js
···
1
-
let wasm;
2
-
3
-
const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
4
-
5
-
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
6
-
7
-
let cachedUint8ArrayMemory0 = null;
8
-
9
-
function getUint8ArrayMemory0() {
10
-
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
11
-
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
12
-
}
13
-
return cachedUint8ArrayMemory0;
14
-
}
15
-
16
-
function getStringFromWasm0(ptr, len) {
17
-
ptr = ptr >>> 0;
18
-
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
19
-
}
20
-
21
-
function addToExternrefTable0(obj) {
22
-
const idx = wasm.__externref_table_alloc();
23
-
wasm.__wbindgen_export_2.set(idx, obj);
24
-
return idx;
25
-
}
26
-
27
-
function handleError(f, args) {
28
-
try {
29
-
return f.apply(this, args);
30
-
} catch (e) {
31
-
const idx = addToExternrefTable0(e);
32
-
wasm.__wbindgen_exn_store(idx);
33
-
}
34
-
}
35
-
36
-
function isLikeNone(x) {
37
-
return x === undefined || x === null;
38
-
}
39
-
40
-
let cachedDataViewMemory0 = null;
41
-
42
-
function getDataViewMemory0() {
43
-
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
44
-
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
45
-
}
46
-
return cachedDataViewMemory0;
47
-
}
48
-
49
-
function getArrayJsValueFromWasm0(ptr, len) {
50
-
ptr = ptr >>> 0;
51
-
const mem = getDataViewMemory0();
52
-
const result = [];
53
-
for (let i = ptr; i < ptr + 4 * len; i += 4) {
54
-
result.push(wasm.__wbindgen_export_2.get(mem.getUint32(i, true)));
55
-
}
56
-
wasm.__externref_drop_slice(ptr, len);
57
-
return result;
58
-
}
59
-
60
-
let WASM_VECTOR_LEN = 0;
61
-
62
-
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
63
-
64
-
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
65
-
? function (arg, view) {
66
-
return cachedTextEncoder.encodeInto(arg, view);
67
-
}
68
-
: function (arg, view) {
69
-
const buf = cachedTextEncoder.encode(arg);
70
-
view.set(buf);
71
-
return {
72
-
read: arg.length,
73
-
written: buf.length
74
-
};
75
-
});
76
-
77
-
function passStringToWasm0(arg, malloc, realloc) {
78
-
79
-
if (realloc === undefined) {
80
-
const buf = cachedTextEncoder.encode(arg);
81
-
const ptr = malloc(buf.length, 1) >>> 0;
82
-
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
83
-
WASM_VECTOR_LEN = buf.length;
84
-
return ptr;
85
-
}
86
-
87
-
let len = arg.length;
88
-
let ptr = malloc(len, 1) >>> 0;
89
-
90
-
const mem = getUint8ArrayMemory0();
91
-
92
-
let offset = 0;
93
-
94
-
for (; offset < len; offset++) {
95
-
const code = arg.charCodeAt(offset);
96
-
if (code > 0x7F) break;
97
-
mem[ptr + offset] = code;
98
-
}
99
-
100
-
if (offset !== len) {
101
-
if (offset !== 0) {
102
-
arg = arg.slice(offset);
103
-
}
104
-
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
105
-
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
106
-
const ret = encodeString(arg, view);
107
-
108
-
offset += ret.written;
109
-
ptr = realloc(ptr, len, offset, 1) >>> 0;
110
-
}
111
-
112
-
WASM_VECTOR_LEN = offset;
113
-
return ptr;
114
-
}
115
-
116
-
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
117
-
? { register: () => {}, unregister: () => {} }
118
-
: new FinalizationRegistry(state => {
119
-
wasm.__wbindgen_export_7.get(state.dtor)(state.a, state.b)
120
-
});
121
-
122
-
function makeClosure(arg0, arg1, dtor, f) {
123
-
const state = { a: arg0, b: arg1, cnt: 1, dtor };
124
-
const real = (...args) => {
125
-
// First up with a closure we increment the internal reference
126
-
// count. This ensures that the Rust closure environment won't
127
-
// be deallocated while we're invoking it.
128
-
state.cnt++;
129
-
try {
130
-
return f(state.a, state.b, ...args);
131
-
} finally {
132
-
if (--state.cnt === 0) {
133
-
wasm.__wbindgen_export_7.get(state.dtor)(state.a, state.b);
134
-
state.a = 0;
135
-
CLOSURE_DTORS.unregister(state);
136
-
}
137
-
}
138
-
};
139
-
real.original = state;
140
-
CLOSURE_DTORS.register(real, state, state);
141
-
return real;
142
-
}
143
-
144
-
function makeMutClosure(arg0, arg1, dtor, f) {
145
-
const state = { a: arg0, b: arg1, cnt: 1, dtor };
146
-
const real = (...args) => {
147
-
// First up with a closure we increment the internal reference
148
-
// count. This ensures that the Rust closure environment won't
149
-
// be deallocated while we're invoking it.
150
-
state.cnt++;
151
-
const a = state.a;
152
-
state.a = 0;
153
-
try {
154
-
return f(a, state.b, ...args);
155
-
} finally {
156
-
if (--state.cnt === 0) {
157
-
wasm.__wbindgen_export_7.get(state.dtor)(a, state.b);
158
-
CLOSURE_DTORS.unregister(state);
159
-
} else {
160
-
state.a = a;
161
-
}
162
-
}
163
-
};
164
-
real.original = state;
165
-
CLOSURE_DTORS.register(real, state, state);
166
-
return real;
167
-
}
168
-
169
-
function debugString(val) {
170
-
// primitive types
171
-
const type = typeof val;
172
-
if (type == 'number' || type == 'boolean' || val == null) {
173
-
return `${val}`;
174
-
}
175
-
if (type == 'string') {
176
-
return `"${val}"`;
177
-
}
178
-
if (type == 'symbol') {
179
-
const description = val.description;
180
-
if (description == null) {
181
-
return 'Symbol';
182
-
} else {
183
-
return `Symbol(${description})`;
184
-
}
185
-
}
186
-
if (type == 'function') {
187
-
const name = val.name;
188
-
if (typeof name == 'string' && name.length > 0) {
189
-
return `Function(${name})`;
190
-
} else {
191
-
return 'Function';
192
-
}
193
-
}
194
-
// objects
195
-
if (Array.isArray(val)) {
196
-
const length = val.length;
197
-
let debug = '[';
198
-
if (length > 0) {
199
-
debug += debugString(val[0]);
200
-
}
201
-
for(let i = 1; i < length; i++) {
202
-
debug += ', ' + debugString(val[i]);
203
-
}
204
-
debug += ']';
205
-
return debug;
206
-
}
207
-
// Test for built-in
208
-
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
209
-
let className;
210
-
if (builtInMatches && builtInMatches.length > 1) {
211
-
className = builtInMatches[1];
212
-
} else {
213
-
// Failed to match the standard '[object ClassName]'
214
-
return toString.call(val);
215
-
}
216
-
if (className == 'Object') {
217
-
// we're a user defined class or Object
218
-
// JSON.stringify avoids problems with cycles, and is generally much
219
-
// easier than looping through ownProperties of `val`.
220
-
try {
221
-
return 'Object(' + JSON.stringify(val) + ')';
222
-
} catch (_) {
223
-
return 'Object';
224
-
}
225
-
}
226
-
// errors
227
-
if (val instanceof Error) {
228
-
return `${val.name}: ${val.message}\n${val.stack}`;
229
-
}
230
-
// TODO we could test for more things here, like `Set`s and `Map`s.
231
-
return className;
232
-
}
233
-
function __wbg_adapter_22(arg0, arg1, arg2) {
234
-
wasm.closure147_externref_shim(arg0, arg1, arg2);
235
-
}
236
-
237
-
function __wbg_adapter_25(arg0, arg1, arg2) {
238
-
wasm.closure241_externref_shim(arg0, arg1, arg2);
239
-
}
240
-
241
-
async function __wbg_load(module, imports) {
242
-
if (typeof Response === 'function' && module instanceof Response) {
243
-
if (typeof WebAssembly.instantiateStreaming === 'function') {
244
-
try {
245
-
return await WebAssembly.instantiateStreaming(module, imports);
246
-
247
-
} catch (e) {
248
-
if (module.headers.get('Content-Type') != 'application/wasm') {
249
-
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
250
-
251
-
} else {
252
-
throw e;
253
-
}
254
-
}
255
-
}
256
-
257
-
const bytes = await module.arrayBuffer();
258
-
return await WebAssembly.instantiate(bytes, imports);
259
-
260
-
} else {
261
-
const instance = await WebAssembly.instantiate(module, imports);
262
-
263
-
if (instance instanceof WebAssembly.Instance) {
264
-
return { instance, module };
265
-
266
-
} else {
267
-
return instance;
268
-
}
269
-
}
270
-
}
271
-
272
-
function __wbg_get_imports() {
273
-
const imports = {};
274
-
imports.wbg = {};
275
-
imports.wbg.__wbg_addEventListener_84ae3eac6e15480a = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
276
-
arg0.addEventListener(getStringFromWasm0(arg1, arg2), arg3, arg4);
277
-
}, arguments) };
278
-
imports.wbg.__wbg_body_942ea927546a04ba = function(arg0) {
279
-
const ret = arg0.body;
280
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
281
-
};
282
-
imports.wbg.__wbg_bubbles_afd8dd1d14b05aba = function(arg0) {
283
-
const ret = arg0.bubbles;
284
-
return ret;
285
-
};
286
-
imports.wbg.__wbg_cachekey_57601dac16343711 = function(arg0) {
287
-
const ret = arg0.__yew_subtree_cache_key;
288
-
return isLikeNone(ret) ? 0x100000001 : (ret) >>> 0;
289
-
};
290
-
imports.wbg.__wbg_call_672a4d21634d4a24 = function() { return handleError(function (arg0, arg1) {
291
-
const ret = arg0.call(arg1);
292
-
return ret;
293
-
}, arguments) };
294
-
imports.wbg.__wbg_cancelBubble_2e66f509cdea4d7e = function(arg0) {
295
-
const ret = arg0.cancelBubble;
296
-
return ret;
297
-
};
298
-
imports.wbg.__wbg_childNodes_c4423003f3a9441f = function(arg0) {
299
-
const ret = arg0.childNodes;
300
-
return ret;
301
-
};
302
-
imports.wbg.__wbg_cloneNode_e35b333b87d51340 = function() { return handleError(function (arg0) {
303
-
const ret = arg0.cloneNode();
304
-
return ret;
305
-
}, arguments) };
306
-
imports.wbg.__wbg_composedPath_977ce97a0ef39358 = function(arg0) {
307
-
const ret = arg0.composedPath();
308
-
return ret;
309
-
};
310
-
imports.wbg.__wbg_createElementNS_914d752e521987da = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
311
-
const ret = arg0.createElementNS(arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
312
-
return ret;
313
-
}, arguments) };
314
-
imports.wbg.__wbg_createElement_8c9931a732ee2fea = function() { return handleError(function (arg0, arg1, arg2) {
315
-
const ret = arg0.createElement(getStringFromWasm0(arg1, arg2));
316
-
return ret;
317
-
}, arguments) };
318
-
imports.wbg.__wbg_createTextNode_42af1a9f21bb3360 = function(arg0, arg1, arg2) {
319
-
const ret = arg0.createTextNode(getStringFromWasm0(arg1, arg2));
320
-
return ret;
321
-
};
322
-
imports.wbg.__wbg_debug_e17b51583ca6a632 = function(arg0, arg1, arg2, arg3) {
323
-
console.debug(arg0, arg1, arg2, arg3);
324
-
};
325
-
imports.wbg.__wbg_document_d249400bd7bd996d = function(arg0) {
326
-
const ret = arg0.document;
327
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
328
-
};
329
-
imports.wbg.__wbg_error_3c7d958458bf649b = function(arg0, arg1) {
330
-
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
331
-
wasm.__wbindgen_free(arg0, arg1 * 4, 4);
332
-
console.error(...v0);
333
-
};
334
-
imports.wbg.__wbg_error_524f506f44df1645 = function(arg0) {
335
-
console.error(arg0);
336
-
};
337
-
imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) {
338
-
let deferred0_0;
339
-
let deferred0_1;
340
-
try {
341
-
deferred0_0 = arg0;
342
-
deferred0_1 = arg1;
343
-
console.error(getStringFromWasm0(arg0, arg1));
344
-
} finally {
345
-
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
346
-
}
347
-
};
348
-
imports.wbg.__wbg_error_80de38b3f7cc3c3c = function(arg0, arg1, arg2, arg3) {
349
-
console.error(arg0, arg1, arg2, arg3);
350
-
};
351
-
imports.wbg.__wbg_fetch_509096533071c657 = function(arg0, arg1) {
352
-
const ret = arg0.fetch(arg1);
353
-
return ret;
354
-
};
355
-
imports.wbg.__wbg_fetch_b7bf320f681242d2 = function(arg0, arg1) {
356
-
const ret = arg0.fetch(arg1);
357
-
return ret;
358
-
};
359
-
imports.wbg.__wbg_from_2a5d3e218e67aa85 = function(arg0) {
360
-
const ret = Array.from(arg0);
361
-
return ret;
362
-
};
363
-
imports.wbg.__wbg_get_67b2ba62fc30de12 = function() { return handleError(function (arg0, arg1) {
364
-
const ret = Reflect.get(arg0, arg1);
365
-
return ret;
366
-
}, arguments) };
367
-
imports.wbg.__wbg_get_b9b93047fe3cf45b = function(arg0, arg1) {
368
-
const ret = arg0[arg1 >>> 0];
369
-
return ret;
370
-
};
371
-
imports.wbg.__wbg_host_166cb082dae71d08 = function(arg0) {
372
-
const ret = arg0.host;
373
-
return ret;
374
-
};
375
-
imports.wbg.__wbg_info_033d8b8a0838f1d3 = function(arg0, arg1, arg2, arg3) {
376
-
console.info(arg0, arg1, arg2, arg3);
377
-
};
378
-
imports.wbg.__wbg_insertBefore_c181fb91844cd959 = function() { return handleError(function (arg0, arg1, arg2) {
379
-
const ret = arg0.insertBefore(arg1, arg2);
380
-
return ret;
381
-
}, arguments) };
382
-
imports.wbg.__wbg_instanceof_Element_0af65443936d5154 = function(arg0) {
383
-
let result;
384
-
try {
385
-
result = arg0 instanceof Element;
386
-
} catch (_) {
387
-
result = false;
388
-
}
389
-
const ret = result;
390
-
return ret;
391
-
};
392
-
imports.wbg.__wbg_instanceof_Error_4d54113b22d20306 = function(arg0) {
393
-
let result;
394
-
try {
395
-
result = arg0 instanceof Error;
396
-
} catch (_) {
397
-
result = false;
398
-
}
399
-
const ret = result;
400
-
return ret;
401
-
};
402
-
imports.wbg.__wbg_instanceof_Response_f2cc20d9f7dfd644 = function(arg0) {
403
-
let result;
404
-
try {
405
-
result = arg0 instanceof Response;
406
-
} catch (_) {
407
-
result = false;
408
-
}
409
-
const ret = result;
410
-
return ret;
411
-
};
412
-
imports.wbg.__wbg_instanceof_ShadowRoot_726578bcd7fa418a = function(arg0) {
413
-
let result;
414
-
try {
415
-
result = arg0 instanceof ShadowRoot;
416
-
} catch (_) {
417
-
result = false;
418
-
}
419
-
const ret = result;
420
-
return ret;
421
-
};
422
-
imports.wbg.__wbg_instanceof_Window_def73ea0955fc569 = function(arg0) {
423
-
let result;
424
-
try {
425
-
result = arg0 instanceof Window;
426
-
} catch (_) {
427
-
result = false;
428
-
}
429
-
const ret = result;
430
-
return ret;
431
-
};
432
-
imports.wbg.__wbg_instanceof_WorkerGlobalScope_dbdbdea7e3b56493 = function(arg0) {
433
-
let result;
434
-
try {
435
-
result = arg0 instanceof WorkerGlobalScope;
436
-
} catch (_) {
437
-
result = false;
438
-
}
439
-
const ret = result;
440
-
return ret;
441
-
};
442
-
imports.wbg.__wbg_is_c7481c65e7e5df9e = function(arg0, arg1) {
443
-
const ret = Object.is(arg0, arg1);
444
-
return ret;
445
-
};
446
-
imports.wbg.__wbg_lastChild_e20d4dc0f9e02ce7 = function(arg0) {
447
-
const ret = arg0.lastChild;
448
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
449
-
};
450
-
imports.wbg.__wbg_length_e2d2a49132c1b256 = function(arg0) {
451
-
const ret = arg0.length;
452
-
return ret;
453
-
};
454
-
imports.wbg.__wbg_listenerid_ed1678830a5b97ec = function(arg0) {
455
-
const ret = arg0.__yew_listener_id;
456
-
return isLikeNone(ret) ? 0x100000001 : (ret) >>> 0;
457
-
};
458
-
imports.wbg.__wbg_log_cad59bb680daec67 = function(arg0, arg1, arg2, arg3) {
459
-
console.log(arg0, arg1, arg2, arg3);
460
-
};
461
-
imports.wbg.__wbg_message_97a2af9b89d693a3 = function(arg0) {
462
-
const ret = arg0.message;
463
-
return ret;
464
-
};
465
-
imports.wbg.__wbg_name_0b327d569f00ebee = function(arg0) {
466
-
const ret = arg0.name;
467
-
return ret;
468
-
};
469
-
imports.wbg.__wbg_namespaceURI_63ddded7f2fdbe94 = function(arg0, arg1) {
470
-
const ret = arg1.namespaceURI;
471
-
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
472
-
var len1 = WASM_VECTOR_LEN;
473
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
474
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
475
-
};
476
-
imports.wbg.__wbg_new_018dcc2d6c8c2f6a = function() { return handleError(function () {
477
-
const ret = new Headers();
478
-
return ret;
479
-
}, arguments) };
480
-
imports.wbg.__wbg_new_405e22f390576ce2 = function() {
481
-
const ret = new Object();
482
-
return ret;
483
-
};
484
-
imports.wbg.__wbg_new_80bf4ee74f41ff92 = function() { return handleError(function () {
485
-
const ret = new URLSearchParams();
486
-
return ret;
487
-
}, arguments) };
488
-
imports.wbg.__wbg_new_8a6f238a6ece86ea = function() {
489
-
const ret = new Error();
490
-
return ret;
491
-
};
492
-
imports.wbg.__wbg_new_9ffbe0a71eff35e3 = function() { return handleError(function (arg0, arg1) {
493
-
const ret = new URL(getStringFromWasm0(arg0, arg1));
494
-
return ret;
495
-
}, arguments) };
496
-
imports.wbg.__wbg_newnoargs_105ed471475aaf50 = function(arg0, arg1) {
497
-
const ret = new Function(getStringFromWasm0(arg0, arg1));
498
-
return ret;
499
-
};
500
-
imports.wbg.__wbg_newwithstr_78e86e03c4ae814e = function() { return handleError(function (arg0, arg1) {
501
-
const ret = new Request(getStringFromWasm0(arg0, arg1));
502
-
return ret;
503
-
}, arguments) };
504
-
imports.wbg.__wbg_newwithstrandinit_06c535e0a867c635 = function() { return handleError(function (arg0, arg1, arg2) {
505
-
const ret = new Request(getStringFromWasm0(arg0, arg1), arg2);
506
-
return ret;
507
-
}, arguments) };
508
-
imports.wbg.__wbg_nextSibling_f17f68d089a20939 = function(arg0) {
509
-
const ret = arg0.nextSibling;
510
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
511
-
};
512
-
imports.wbg.__wbg_ok_3aaf32d069979723 = function(arg0) {
513
-
const ret = arg0.ok;
514
-
return ret;
515
-
};
516
-
imports.wbg.__wbg_outerHTML_69175e02bad1633b = function(arg0, arg1) {
517
-
const ret = arg1.outerHTML;
518
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
519
-
const len1 = WASM_VECTOR_LEN;
520
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
521
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
522
-
};
523
-
imports.wbg.__wbg_parentElement_be28a1a931f9c9b7 = function(arg0) {
524
-
const ret = arg0.parentElement;
525
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
526
-
};
527
-
imports.wbg.__wbg_parentNode_9de97a0e7973ea4e = function(arg0) {
528
-
const ret = arg0.parentNode;
529
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
530
-
};
531
-
imports.wbg.__wbg_queueMicrotask_97d92b4fcc8a61c5 = function(arg0) {
532
-
queueMicrotask(arg0);
533
-
};
534
-
imports.wbg.__wbg_queueMicrotask_d3219def82552485 = function(arg0) {
535
-
const ret = arg0.queueMicrotask;
536
-
return ret;
537
-
};
538
-
imports.wbg.__wbg_removeAttribute_e419cd6726b4c62f = function() { return handleError(function (arg0, arg1, arg2) {
539
-
arg0.removeAttribute(getStringFromWasm0(arg1, arg2));
540
-
}, arguments) };
541
-
imports.wbg.__wbg_removeChild_841bf1dc802c0a2c = function() { return handleError(function (arg0, arg1) {
542
-
const ret = arg0.removeChild(arg1);
543
-
return ret;
544
-
}, arguments) };
545
-
imports.wbg.__wbg_removeEventListener_d365ee1c2a7b08f0 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
546
-
arg0.removeEventListener(getStringFromWasm0(arg1, arg2), arg3, arg4 !== 0);
547
-
}, arguments) };
548
-
imports.wbg.__wbg_resolve_4851785c9c5f573d = function(arg0) {
549
-
const ret = Promise.resolve(arg0);
550
-
return ret;
551
-
};
552
-
imports.wbg.__wbg_search_e0e79cfe010c5c23 = function(arg0, arg1) {
553
-
const ret = arg1.search;
554
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
555
-
const len1 = WASM_VECTOR_LEN;
556
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
557
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
558
-
};
559
-
imports.wbg.__wbg_setAttribute_2704501201f15687 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
560
-
arg0.setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
561
-
}, arguments) };
562
-
imports.wbg.__wbg_set_bb8cecf6a62b9f46 = function() { return handleError(function (arg0, arg1, arg2) {
563
-
const ret = Reflect.set(arg0, arg1, arg2);
564
-
return ret;
565
-
}, arguments) };
566
-
imports.wbg.__wbg_setcachekey_bb5f908a0e3ee714 = function(arg0, arg1) {
567
-
arg0.__yew_subtree_cache_key = arg1 >>> 0;
568
-
};
569
-
imports.wbg.__wbg_setcapture_46bd7043887eba02 = function(arg0, arg1) {
570
-
arg0.capture = arg1 !== 0;
571
-
};
572
-
imports.wbg.__wbg_setchecked_5024c3767a6970c2 = function(arg0, arg1) {
573
-
arg0.checked = arg1 !== 0;
574
-
};
575
-
imports.wbg.__wbg_setheaders_834c0bdb6a8949ad = function(arg0, arg1) {
576
-
arg0.headers = arg1;
577
-
};
578
-
imports.wbg.__wbg_setinnerHTML_31bde41f835786f7 = function(arg0, arg1, arg2) {
579
-
arg0.innerHTML = getStringFromWasm0(arg1, arg2);
580
-
};
581
-
imports.wbg.__wbg_setlistenerid_3d14d37a42484593 = function(arg0, arg1) {
582
-
arg0.__yew_listener_id = arg1 >>> 0;
583
-
};
584
-
imports.wbg.__wbg_setmethod_3c5280fe5d890842 = function(arg0, arg1, arg2) {
585
-
arg0.method = getStringFromWasm0(arg1, arg2);
586
-
};
587
-
imports.wbg.__wbg_setnodeValue_58cb1b2f6b6c33d2 = function(arg0, arg1, arg2) {
588
-
arg0.nodeValue = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2);
589
-
};
590
-
imports.wbg.__wbg_setpassive_57a5a4c4b00a7c62 = function(arg0, arg1) {
591
-
arg0.passive = arg1 !== 0;
592
-
};
593
-
imports.wbg.__wbg_setsearch_609451e9e712f3c6 = function(arg0, arg1, arg2) {
594
-
arg0.search = getStringFromWasm0(arg1, arg2);
595
-
};
596
-
imports.wbg.__wbg_setsubtreeid_32b8ceff55862e29 = function(arg0, arg1) {
597
-
arg0.__yew_subtree_id = arg1 >>> 0;
598
-
};
599
-
imports.wbg.__wbg_setvalue_08d17a42e5d5069d = function(arg0, arg1, arg2) {
600
-
arg0.value = getStringFromWasm0(arg1, arg2);
601
-
};
602
-
imports.wbg.__wbg_setvalue_6ad9ef6c692ea746 = function(arg0, arg1, arg2) {
603
-
arg0.value = getStringFromWasm0(arg1, arg2);
604
-
};
605
-
imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) {
606
-
const ret = arg1.stack;
607
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
608
-
const len1 = WASM_VECTOR_LEN;
609
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
610
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
611
-
};
612
-
imports.wbg.__wbg_static_accessor_GLOBAL_88a902d13a557d07 = function() {
613
-
const ret = typeof global === 'undefined' ? null : global;
614
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
615
-
};
616
-
imports.wbg.__wbg_static_accessor_GLOBAL_THIS_56578be7e9f832b0 = function() {
617
-
const ret = typeof globalThis === 'undefined' ? null : globalThis;
618
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
619
-
};
620
-
imports.wbg.__wbg_static_accessor_SELF_37c5d418e4bf5819 = function() {
621
-
const ret = typeof self === 'undefined' ? null : self;
622
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
623
-
};
624
-
imports.wbg.__wbg_static_accessor_WINDOW_5de37043a91a9c40 = function() {
625
-
const ret = typeof window === 'undefined' ? null : window;
626
-
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
627
-
};
628
-
imports.wbg.__wbg_statusText_207754230b39e67c = function(arg0, arg1) {
629
-
const ret = arg1.statusText;
630
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
631
-
const len1 = WASM_VECTOR_LEN;
632
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
633
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
634
-
};
635
-
imports.wbg.__wbg_subtreeid_e65dfcc52d403fd9 = function(arg0) {
636
-
const ret = arg0.__yew_subtree_id;
637
-
return isLikeNone(ret) ? 0x100000001 : (ret) >>> 0;
638
-
};
639
-
imports.wbg.__wbg_textContent_215d0f87d539368a = function(arg0, arg1) {
640
-
const ret = arg1.textContent;
641
-
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
642
-
var len1 = WASM_VECTOR_LEN;
643
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
644
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
645
-
};
646
-
imports.wbg.__wbg_text_7805bea50de2af49 = function() { return handleError(function (arg0) {
647
-
const ret = arg0.text();
648
-
return ret;
649
-
}, arguments) };
650
-
imports.wbg.__wbg_then_44b73946d2fb3e7d = function(arg0, arg1) {
651
-
const ret = arg0.then(arg1);
652
-
return ret;
653
-
};
654
-
imports.wbg.__wbg_then_48b406749878a531 = function(arg0, arg1, arg2) {
655
-
const ret = arg0.then(arg1, arg2);
656
-
return ret;
657
-
};
658
-
imports.wbg.__wbg_toString_5285597960676b7b = function(arg0) {
659
-
const ret = arg0.toString();
660
-
return ret;
661
-
};
662
-
imports.wbg.__wbg_toString_c813bbd34d063839 = function(arg0) {
663
-
const ret = arg0.toString();
664
-
return ret;
665
-
};
666
-
imports.wbg.__wbg_url_8f9653b899456042 = function(arg0, arg1) {
667
-
const ret = arg1.url;
668
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
669
-
const len1 = WASM_VECTOR_LEN;
670
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
671
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
672
-
};
673
-
imports.wbg.__wbg_value_1d971aac958c6f2f = function(arg0, arg1) {
674
-
const ret = arg1.value;
675
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
676
-
const len1 = WASM_VECTOR_LEN;
677
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
678
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
679
-
};
680
-
imports.wbg.__wbg_value_91cbf0dd3ab84c1e = function(arg0, arg1) {
681
-
const ret = arg1.value;
682
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
683
-
const len1 = WASM_VECTOR_LEN;
684
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
685
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
686
-
};
687
-
imports.wbg.__wbg_warn_aaf1f4664a035bd6 = function(arg0, arg1, arg2, arg3) {
688
-
console.warn(arg0, arg1, arg2, arg3);
689
-
};
690
-
imports.wbg.__wbindgen_cb_drop = function(arg0) {
691
-
const obj = arg0.original;
692
-
if (obj.cnt-- == 1) {
693
-
obj.a = 0;
694
-
return true;
695
-
}
696
-
const ret = false;
697
-
return ret;
698
-
};
699
-
imports.wbg.__wbindgen_closure_wrapper1517 = function(arg0, arg1, arg2) {
700
-
const ret = makeClosure(arg0, arg1, 148, __wbg_adapter_22);
701
-
return ret;
702
-
};
703
-
imports.wbg.__wbindgen_closure_wrapper3717 = function(arg0, arg1, arg2) {
704
-
const ret = makeMutClosure(arg0, arg1, 242, __wbg_adapter_25);
705
-
return ret;
706
-
};
707
-
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
708
-
const ret = debugString(arg1);
709
-
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
710
-
const len1 = WASM_VECTOR_LEN;
711
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
712
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
713
-
};
714
-
imports.wbg.__wbindgen_init_externref_table = function() {
715
-
const table = wasm.__wbindgen_export_2;
716
-
const offset = table.grow(4);
717
-
table.set(0, undefined);
718
-
table.set(offset + 0, undefined);
719
-
table.set(offset + 1, null);
720
-
table.set(offset + 2, true);
721
-
table.set(offset + 3, false);
722
-
;
723
-
};
724
-
imports.wbg.__wbindgen_is_function = function(arg0) {
725
-
const ret = typeof(arg0) === 'function';
726
-
return ret;
727
-
};
728
-
imports.wbg.__wbindgen_is_undefined = function(arg0) {
729
-
const ret = arg0 === undefined;
730
-
return ret;
731
-
};
732
-
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
733
-
const obj = arg1;
734
-
const ret = typeof(obj) === 'string' ? obj : undefined;
735
-
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
736
-
var len1 = WASM_VECTOR_LEN;
737
-
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
738
-
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
739
-
};
740
-
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
741
-
const ret = getStringFromWasm0(arg0, arg1);
742
-
return ret;
743
-
};
744
-
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
745
-
throw new Error(getStringFromWasm0(arg0, arg1));
746
-
};
747
-
748
-
return imports;
749
-
}
750
-
751
-
function __wbg_init_memory(imports, memory) {
752
-
753
-
}
754
-
755
-
function __wbg_finalize_init(instance, module) {
756
-
wasm = instance.exports;
757
-
__wbg_init.__wbindgen_wasm_module = module;
758
-
cachedDataViewMemory0 = null;
759
-
cachedUint8ArrayMemory0 = null;
760
-
761
-
762
-
wasm.__wbindgen_start();
763
-
return wasm;
764
-
}
765
-
766
-
function initSync(module) {
767
-
if (wasm !== undefined) return wasm;
768
-
769
-
770
-
if (typeof module !== 'undefined') {
771
-
if (Object.getPrototypeOf(module) === Object.prototype) {
772
-
({module} = module)
773
-
} else {
774
-
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
775
-
}
776
-
}
777
-
778
-
const imports = __wbg_get_imports();
779
-
780
-
__wbg_init_memory(imports);
781
-
782
-
if (!(module instanceof WebAssembly.Module)) {
783
-
module = new WebAssembly.Module(module);
784
-
}
785
-
786
-
const instance = new WebAssembly.Instance(module, imports);
787
-
788
-
return __wbg_finalize_init(instance, module);
789
-
}
790
-
791
-
async function __wbg_init(module_or_path) {
792
-
if (wasm !== undefined) return wasm;
793
-
794
-
795
-
if (typeof module_or_path !== 'undefined') {
796
-
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
797
-
({module_or_path} = module_or_path)
798
-
} else {
799
-
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
800
-
}
801
-
}
802
-
803
-
if (typeof module_or_path === 'undefined') {
804
-
module_or_path = new URL('app_demo_bg.wasm', import.meta.url);
805
-
}
806
-
const imports = __wbg_get_imports();
807
-
808
-
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
809
-
module_or_path = fetch(module_or_path);
810
-
}
811
-
812
-
__wbg_init_memory(imports);
813
-
814
-
const { instance, module } = await __wbg_load(await module_or_path, imports);
815
-
816
-
return __wbg_finalize_init(instance, module);
817
-
}
818
-
819
-
export { initSync };
820
-
export default __wbg_init;
rust_demo_app/app_demo/dist/app_demo-814a61e8321d189a_bg.wasm
rust_demo_app/app_demo/dist/app_demo-814a61e8321d189a_bg.wasm
This is a binary file and will not be displayed.
-155
rust_demo_app/app_demo/dist/index.html
-155
rust_demo_app/app_demo/dist/index.html
···
1
-
<!DOCTYPE html>
2
-
<html>
3
-
4
-
<head>
5
-
<meta charset="utf-8" />
6
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7
-
<title>Rust Counter Demo</title>
8
-
<link rel="stylesheet" href="/styles-1710c094e1b1c697.css" integrity="sha384-SVCA9/fxaw0hSXsKWVyzm4qWX7BBmBapjqORc/QpnRRRQF1rvYedexH0fPZ7Asjo"/>
9
-
<link rel="modulepreload" href="/app_demo-814a61e8321d189a.js" crossorigin="anonymous" integrity="sha384-17HNJnOlv+fhkn8qgl+DdcrDmKclle4eSfXVqtwHwRXnusAgAOd62U7tvU72+w7o"><link rel="preload" href="/app_demo-814a61e8321d189a_bg.wasm" crossorigin="anonymous" integrity="sha384-7xMFYwhxnoMiFToa6HonKzQ/qhX383B/UqRDZ/Tf3SPcyPsa5a3FXNt8FKVHIH0D" as="fetch" type="application/wasm"></head>
10
-
11
-
<body>
12
-
<!-- Yew app will mount here -->
13
-
14
-
<script type="module">
15
-
import init, * as bindings from '/app_demo-814a61e8321d189a.js';
16
-
const wasm = await init({ module_or_path: '/app_demo-814a61e8321d189a_bg.wasm' });
17
-
18
-
19
-
window.wasmBindings = bindings;
20
-
21
-
22
-
dispatchEvent(new CustomEvent("TrunkApplicationStarted", {detail: {wasm}}));
23
-
24
-
</script><script>"use strict";
25
-
26
-
(function () {
27
-
28
-
const address = '{{__TRUNK_ADDRESS__}}';
29
-
const base = '{{__TRUNK_WS_BASE__}}';
30
-
let protocol = '';
31
-
protocol =
32
-
protocol
33
-
? protocol
34
-
: window.location.protocol === 'https:'
35
-
? 'wss'
36
-
: 'ws';
37
-
const url = protocol + '://' + address + base + '.well-known/trunk/ws';
38
-
39
-
class Overlay {
40
-
constructor() {
41
-
// create an overlay
42
-
this._overlay = document.createElement("div");
43
-
const style = this._overlay.style;
44
-
style.height = "100vh";
45
-
style.width = "100vw";
46
-
style.position = "fixed";
47
-
style.top = "0";
48
-
style.left = "0";
49
-
style.backgroundColor = "rgba(222, 222, 222, 0.5)";
50
-
style.fontFamily = "sans-serif";
51
-
// not sure that's the right approach
52
-
style.zIndex = "1000000";
53
-
style.backdropFilter = "blur(1rem)";
54
-
55
-
const container = document.createElement("div");
56
-
// center it
57
-
container.style.position = "absolute";
58
-
container.style.top = "30%";
59
-
container.style.left = "15%";
60
-
container.style.maxWidth = "85%";
61
-
62
-
this._title = document.createElement("div");
63
-
this._title.innerText = "Build failure";
64
-
this._title.style.paddingBottom = "2rem";
65
-
this._title.style.fontSize = "2.5rem";
66
-
67
-
this._message = document.createElement("div");
68
-
this._message.style.whiteSpace = "pre-wrap";
69
-
70
-
const icon= document.createElement("div");
71
-
icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="#dc3545" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg>';
72
-
this._title.prepend(icon);
73
-
74
-
container.append(this._title, this._message);
75
-
this._overlay.append(container);
76
-
77
-
this._inject();
78
-
window.setInterval(() => {
79
-
this._inject();
80
-
}, 250);
81
-
}
82
-
83
-
set reason(reason) {
84
-
this._message.textContent = reason;
85
-
}
86
-
87
-
_inject() {
88
-
if (!this._overlay.isConnected) {
89
-
// prepend it
90
-
document.body?.prepend(this._overlay);
91
-
}
92
-
}
93
-
94
-
}
95
-
96
-
class Client {
97
-
constructor(url) {
98
-
this.url = url;
99
-
this.poll_interval = 5000;
100
-
this._overlay = null;
101
-
}
102
-
103
-
start() {
104
-
const ws = new WebSocket(this.url);
105
-
ws.onmessage = (ev) => {
106
-
const msg = JSON.parse(ev.data);
107
-
switch (msg.type) {
108
-
case "reload":
109
-
this.reload();
110
-
break;
111
-
case "buildFailure":
112
-
this.buildFailure(msg.data)
113
-
break;
114
-
}
115
-
};
116
-
ws.onclose = () => this.onclose();
117
-
}
118
-
119
-
onclose() {
120
-
window.setTimeout(
121
-
() => {
122
-
// when we successfully reconnect, we'll force a
123
-
// reload (since we presumably lost connection to
124
-
// trunk due to it being killed, so it will have
125
-
// rebuilt on restart)
126
-
const ws = new WebSocket(this.url);
127
-
ws.onopen = () => window.location.reload();
128
-
ws.onclose = () => this.onclose();
129
-
},
130
-
this.poll_interval);
131
-
}
132
-
133
-
reload() {
134
-
window.location.reload();
135
-
}
136
-
137
-
buildFailure({reason}) {
138
-
// also log the console
139
-
console.error("Build failed:", reason);
140
-
141
-
console.debug("Overlay", this._overlay);
142
-
143
-
if (!this._overlay) {
144
-
this._overlay = new Overlay();
145
-
}
146
-
this._overlay.reason = reason;
147
-
}
148
-
}
149
-
150
-
new Client(url).start();
151
-
152
-
})()
153
-
</script></body>
154
-
155
-
</html>
-102
rust_demo_app/app_demo/dist/styles-1710c094e1b1c697.css
-102
rust_demo_app/app_demo/dist/styles-1710c094e1b1c697.css
···
1
-
@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&display=swap');
2
-
3
-
:root {
4
-
--primary-color: #5c6bc0;
5
-
--secondary-color: #8e99f3;
6
-
--bg-color: #f5f5f5;
7
-
--text-color: #333;
8
-
--success-color: #4caf50;
9
-
--error-color: #f44336;
10
-
--font-mono: 'Fira Code', monospace;
11
-
}
12
-
13
-
body {
14
-
font-family: var(--font-mono);
15
-
background-color: var(--bg-color);
16
-
color: var(--text-color);
17
-
display: flex;
18
-
justify-content: center;
19
-
align-items: center;
20
-
min-height: 100vh;
21
-
margin: 0;
22
-
padding: 20px;
23
-
}
24
-
25
-
.counter-app {
26
-
background-color: white;
27
-
border-radius: 12px;
28
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
29
-
padding: 2rem;
30
-
width: 100%;
31
-
max-width: 500px;
32
-
text-align: center;
33
-
}
34
-
35
-
h1 {
36
-
color: var(--primary-color);
37
-
margin-bottom: 1.5rem;
38
-
font-family: var(--font-mono);
39
-
font-weight: 600;
40
-
letter-spacing: -0.5px;
41
-
}
42
-
43
-
.counter-value {
44
-
font-size: 2rem;
45
-
font-weight: 600;
46
-
margin: 2rem 0;
47
-
font-family: var(--font-mono);
48
-
letter-spacing: -0.5px;
49
-
}
50
-
51
-
.counter-value.loading {
52
-
color: var(--secondary-color);
53
-
}
54
-
55
-
.error-message {
56
-
color: var(--error-color);
57
-
background-color: rgba(244, 67, 54, 0.1);
58
-
border-radius: 4px;
59
-
padding: 0.75rem;
60
-
margin: 1rem 0;
61
-
font-family: var(--font-mono);
62
-
}
63
-
64
-
.button-group {
65
-
display: flex;
66
-
gap: 1rem;
67
-
justify-content: center;
68
-
margin-top: 2rem;
69
-
}
70
-
71
-
button {
72
-
background-color: var(--primary-color);
73
-
border: none;
74
-
border-radius: 4px;
75
-
color: white;
76
-
cursor: pointer;
77
-
font-size: 1rem;
78
-
font-weight: 500;
79
-
padding: 0.75rem 1.5rem;
80
-
transition: all 0.2s;
81
-
font-family: var(--font-mono);
82
-
letter-spacing: 0.5px;
83
-
}
84
-
85
-
button:hover {
86
-
background-color: var(--secondary-color);
87
-
transform: translateY(-2px);
88
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
89
-
}
90
-
91
-
button:active {
92
-
transform: translateY(0);
93
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
94
-
}
95
-
96
-
button.reset {
97
-
background-color: var(--error-color);
98
-
}
99
-
100
-
button.reset:hover {
101
-
background-color: #e53935;
102
-
}
+101
-45
rust_demo_app/app_demo/src/main.rs
+101
-45
rust_demo_app/app_demo/src/main.rs
···
5
5
use types_demo::CounterState;
6
6
use wasm_bindgen_futures::spawn_local;
7
7
use yew::prelude::*;
8
+
use yew_router::prelude::*;
9
+
use yewdux::prelude::*;
10
+
11
+
// Import the Store trait
12
+
use atrium_common::store::Store;
8
13
9
14
mod atrium_stores;
10
15
mod idb;
11
16
mod oauth_client;
17
+
18
+
// Add pages module
19
+
pub mod pages;
20
+
21
+
// Import page components
22
+
use crate::pages::callback::CallbackPage;
23
+
use crate::pages::home::HomePage;
24
+
use crate::pages::login::LoginPage;
25
+
26
+
// Import oauth client
27
+
use crate::oauth_client::get_oauth_client;
28
+
use atrium_api::agent::Agent;
29
+
use atrium_api::types::string::Did;
12
30
13
31
const API_ROOT: &str = "";
14
32
···
66
84
}
67
85
}
68
86
87
+
// Yewdux Store for Authentication State
88
+
#[derive(Default, Clone, PartialEq, Store)]
89
+
pub struct AuthStore {
90
+
pub did: Option<Did>, // Store the user's DID
91
+
// We could store the agent here too, but it's not directly serializable by default for Store
92
+
// So we might need a transient way or reconstruct it. For now, just DID.
93
+
// #[store(skip_serializing)] // If we were to store Agent here
94
+
// pub agent: Option<Arc<Agent<crate::oauth_client::OAuthClientType>>>, // This might be complex due to agent's client type
95
+
}
96
+
97
+
// Define the routes
98
+
#[derive(Clone, Routable, PartialEq)]
99
+
pub enum AppRoute {
100
+
#[at("/")]
101
+
Home,
102
+
#[at("/login")]
103
+
Login,
104
+
#[at("/oauth/callback")]
105
+
Callback,
106
+
#[not_found]
107
+
#[at("/404")]
108
+
NotFound,
109
+
}
110
+
111
+
fn switch(routes: AppRoute) -> Html {
112
+
match routes {
113
+
AppRoute::Home => html! { <HomePage /> },
114
+
AppRoute::Login => html! { <LoginPage /> },
115
+
AppRoute::Callback => html! { <CallbackPage /> },
116
+
AppRoute::NotFound => html! { <h1>{ "404 Not Found" }</h1> },
117
+
}
118
+
}
119
+
69
120
#[function_component(App)]
70
-
fn app() -> Html {
71
-
let state = use_reducer(AppState::default);
121
+
pub fn app() -> Html {
122
+
let (auth_store, auth_dispatch) = use_store::<AuthStore>();
123
+
let navigator = use_navigator();
72
124
73
-
// Initial load effect
125
+
// Effect for session restoration
74
126
{
75
-
let state = state.clone();
127
+
let auth_dispatch = auth_dispatch.clone(); // auth_dispatch for the effect
128
+
let auth_store_for_effect = auth_store.clone(); // auth_store for the effect
76
129
use_effect_with((), move |_| {
77
-
state.dispatch(CounterAction::LoadCounter);
78
-
fetch_counter(state);
130
+
log::info!("App loaded. AuthStore DID: {:?}", auth_store_for_effect.did);
131
+
// ... session restoration logic will go here ...
79
132
|| ()
80
133
});
81
134
}
82
135
83
-
// Define event handlers
84
-
let on_reset_click = {
85
-
let state = state.clone();
136
+
// Clone auth_store for the logout callback
137
+
let auth_store_for_logout = auth_store.clone();
138
+
let on_logout = {
139
+
let auth_dispatch = auth_dispatch.clone(); // auth_dispatch for logout
140
+
let navigator = navigator.clone();
141
+
// auth_store_for_logout is already cloned above and will be captured by the closure
86
142
Callback::from(move |_| {
87
-
state.dispatch(CounterAction::ResetRequested);
88
-
reset_counter(state.clone());
89
-
})
90
-
};
143
+
let auth_dispatch = auth_dispatch.clone(); // further clone for spawn_local
144
+
let navigator = navigator.clone(); // further clone for spawn_local
145
+
let auth_store_captured = auth_store_for_logout.clone(); // clone for spawn_local
91
146
92
-
let on_increment_click = {
93
-
let state = state.clone();
94
-
Callback::from(move |_| {
95
-
// Special handling for value 10
96
-
if let Some(counter) = &state.counter {
97
-
if counter.value == 10 {
98
-
state.dispatch(CounterAction::ClearError);
99
-
}
147
+
if let Some(did_to_logout) = auth_store_captured.did.clone() {
148
+
yew::platform::spawn_local(async move {
149
+
let store = crate::atrium_stores::IndexDBSessionStore::new();
150
+
match store.del(&did_to_logout).await {
151
+
Ok(_) => {
152
+
log::info!("Session deleted from store for DID: {:?}", did_to_logout)
153
+
}
154
+
Err(e) => log::error!("Failed to delete session from store: {:?}", e),
155
+
}
156
+
auth_dispatch.set(AuthStore { did: None });
157
+
if let Some(nav) = &navigator {
158
+
nav.push(&AppRoute::Home);
159
+
}
160
+
});
100
161
}
101
-
increment_counter(state.clone());
102
162
})
103
163
};
104
164
105
-
// Render counter display
106
-
let counter_display = match &state.counter {
107
-
Some(counter) => {
108
-
html! { <p class="counter-value">{ format!("Current value: {}", counter.value) }</p> }
109
-
}
110
-
None => html! { <p class="counter-value loading">{ "Loading..." }</p> },
111
-
};
112
-
113
-
// Render error message if any
114
-
let error_display = match &state.error {
115
-
Some(err) => html! { <div class="error-message">{ format!("Error: {}", err) }</div> },
116
-
None => html! {<> </>},
117
-
};
118
-
119
165
html! {
120
-
<div class="counter-app">
121
-
<h1>{ "Rust Counter Demo" }</h1>
122
-
{ counter_display }
123
-
{ error_display }
124
-
<div class="button-group">
125
-
<button class="reset" onclick={on_reset_click}>{ "Reset" }</button>
126
-
<button onclick={on_increment_click}>{ "Increment" }</button>
127
-
</div>
128
-
</div>
166
+
<BrowserRouter>
167
+
<nav class="navbar">
168
+
<div class="navbar-start">
169
+
<Link<AppRoute> to={AppRoute::Home} classes="btn btn-ghost normal-case text-xl">
170
+
{ "Rust ATProto Counter" }
171
+
</Link<AppRoute>>
172
+
</div>
173
+
<div class="navbar-end">
174
+
if auth_store.did.is_some() {
175
+
<button onclick={on_logout} class="btn btn-ghost">{ "Logout" }</button>
176
+
} else {
177
+
<Link<AppRoute> to={AppRoute::Login} classes="btn btn-ghost">{ "Login" }</Link<AppRoute>>
178
+
}
179
+
</div>
180
+
</nav>
181
+
<main class="container mx-auto p-4">
182
+
<Switch<AppRoute> render={switch} />
183
+
</main>
184
+
</BrowserRouter>
129
185
}
130
186
}
131
187
+1
rust_demo_app/app_demo/src/oauth_client.rs
+1
rust_demo_app/app_demo/src/oauth_client.rs
···
131
131
redirect_uris: Some(vec![format!("{}/oauth/callback", origin)]),
132
132
scopes: Some(vec![
133
133
Scope::Known(KnownScope::Atproto), // Full access to user's account
134
+
Scope::Known(KnownScope::TransitionGeneric), // Added this scope
134
135
// Add other scopes if needed, e.g., for specific lexicons if ATProto supports granular scopes later
135
136
]),
136
137
// Other fields like client_name, logo_uri can be added
+129
rust_demo_app/app_demo/src/pages/callback.rs
+129
rust_demo_app/app_demo/src/pages/callback.rs
···
1
+
use wasm_bindgen_futures::spawn_local;
2
+
use yew::prelude::*;
3
+
use yew_router::prelude::*;
4
+
use yewdux::prelude::*;
5
+
6
+
use crate::oauth_client::get_oauth_client;
7
+
use crate::{AppRoute, AuthStore}; // Assuming AppRoute and AuthStore are in lib.rs or main.rs and pub
8
+
9
+
// Import Store trait for state_store.get()
10
+
use atrium_common::store::Store;
11
+
// Import for parsing query string into CallbackParams
12
+
use atrium_oauth::CallbackParams; // Assuming this is the correct path for 0.1.1
13
+
// We might need IndexDBStateStore if we manually delete the state after successful callback
14
+
use crate::atrium_stores::IndexDBStateStore;
15
+
// Import Agent to make authenticated calls
16
+
use atrium_api::agent::Agent;
17
+
18
+
#[function_component(CallbackPage)]
19
+
pub fn callback_page() -> Html {
20
+
let (_, auth_dispatch) = use_store::<AuthStore>();
21
+
let navigator = use_navigator().unwrap(); // Should always be available in a routed context
22
+
let location = use_location().unwrap(); // For accessing query parameters
23
+
24
+
let error_message = use_state(|| None::<String>);
25
+
26
+
{
27
+
let auth_dispatch = auth_dispatch.clone();
28
+
let navigator = navigator.clone();
29
+
let error_message = error_message.clone();
30
+
31
+
use_effect_with((location.query_str().to_string(),), move |(query_str,)| {
32
+
let query_str = query_str.clone(); // Ensure query_str is owned for the async block
33
+
let auth_dispatch = auth_dispatch.clone();
34
+
let navigator = navigator.clone();
35
+
let error_message = error_message.clone();
36
+
37
+
spawn_local(async move {
38
+
let params: CallbackParams = match serde_html_form::from_str(&query_str) {
39
+
Ok(p) => p,
40
+
Err(e) => {
41
+
log::error!("Failed to parse callback query parameters: {:?}", e);
42
+
error_message
43
+
.set(Some(format!("Error parsing callback parameters: {:?}", e)));
44
+
// Optionally redirect to login or show error
45
+
navigator.push(&AppRoute::Login);
46
+
return;
47
+
}
48
+
};
49
+
50
+
// The `state` value within `params` will be used by `client.callback()`
51
+
// to retrieve necessary data (like PKCE verifier and original PDS host) from the state_store.
52
+
let state_from_query = params.state.clone(); // For potential manual deletion later
53
+
54
+
let oauth_client = get_oauth_client().await;
55
+
56
+
match oauth_client.callback(params).await {
57
+
Ok((session_data, _optional_dpop_nonce)) => {
58
+
log::info!("OAuth callback successful, creating Agent...");
59
+
60
+
// Create Agent from the session
61
+
// Note: The concrete type of Agent might depend on how OAuthSession implements SessionManager
62
+
// Or Agent::new might be flexible enough.
63
+
let agent = Agent::new(session_data);
64
+
65
+
// Get session info using the agent
66
+
match agent.api.com.atproto.server.get_session().await {
67
+
Ok(session_info) => {
68
+
log::info!(
69
+
"Successfully retrieved session info. DID: {:?}, Handle: {:?}",
70
+
session_info.did,
71
+
session_info.handle
72
+
);
73
+
// Extract the DID from the getSession response
74
+
let user_did = session_info.did.clone(); // Clone the Did
75
+
76
+
// Store the cloned DID
77
+
auth_dispatch.set(AuthStore {
78
+
did: Some(user_did),
79
+
}); // No need to clone again here
80
+
81
+
// Attempt to delete the used state from the state store (keep this logic)
82
+
let state_store = IndexDBStateStore::new();
83
+
if let Some(state_key_to_delete) = state_from_query {
84
+
if let Err(e) = state_store.del(&state_key_to_delete).await {
85
+
log::warn!(
86
+
"Failed to delete state from store after use: {:?}",
87
+
e
88
+
);
89
+
}
90
+
} else {
91
+
log::warn!(
92
+
"No state found in CallbackParams to delete from store."
93
+
);
94
+
}
95
+
navigator.push(&AppRoute::Home);
96
+
}
97
+
Err(e) => {
98
+
log::error!(
99
+
"Failed to get session info using agent after successful callback: {:?}",
100
+
e
101
+
);
102
+
error_message
103
+
.set(Some(format!("Failed to verify session: {:?}", e)));
104
+
navigator.push(&AppRoute::Login);
105
+
}
106
+
}
107
+
}
108
+
Err(e) => {
109
+
log::error!("OAuth callback processing failed: {:?}", e);
110
+
error_message.set(Some(format!("Login failed during callback: {:?}", e)));
111
+
navigator.push(&AppRoute::Login);
112
+
}
113
+
}
114
+
});
115
+
|| ()
116
+
});
117
+
}
118
+
119
+
html! {
120
+
<div>
121
+
if let Some(err) = &*error_message {
122
+
<p class="text-red-500">{ format!("Error during login: {}", err) }</p>
123
+
<p><Link<AppRoute> to={AppRoute::Login}>{ "Try logging in again" }</Link<AppRoute>></p>
124
+
} else {
125
+
<p>{ "Processing login, please wait..." }</p>
126
+
}
127
+
</div>
128
+
}
129
+
}
+11
rust_demo_app/app_demo/src/pages/home.rs
+11
rust_demo_app/app_demo/src/pages/home.rs
+105
rust_demo_app/app_demo/src/pages/login.rs
+105
rust_demo_app/app_demo/src/pages/login.rs
···
1
+
use gloo::utils::window;
2
+
use wasm_bindgen_futures::spawn_local;
3
+
use web_sys::HtmlInputElement;
4
+
use yew::prelude::*;
5
+
6
+
use crate::oauth_client::get_oauth_client;
7
+
// use crate::AppRoute; // Removed unused import
8
+
use atrium_oauth::{AuthorizeOptions, KnownScope, Scope};
9
+
10
+
#[function_component(LoginPage)]
11
+
pub fn login_page() -> Html {
12
+
let pds_host = use_state(|| "https://bsky.social".to_string());
13
+
let error_message = use_state(|| None::<String>);
14
+
15
+
let on_pds_host_input = {
16
+
let pds_host = pds_host.clone();
17
+
Callback::from(move |e: InputEvent| {
18
+
let input: HtmlInputElement = e.target_unchecked_into();
19
+
pds_host.set(input.value());
20
+
})
21
+
};
22
+
23
+
let on_submit = {
24
+
let pds_host = pds_host.clone();
25
+
let error_message = error_message.clone();
26
+
27
+
Callback::from(move |e: SubmitEvent| {
28
+
e.prevent_default();
29
+
let pds_host_val = pds_host.trim();
30
+
if pds_host_val.is_empty() {
31
+
error_message.set(Some("PDS Host cannot be empty".to_string()));
32
+
return;
33
+
}
34
+
if !pds_host_val.starts_with("https://") && !pds_host_val.starts_with("http://") {
35
+
error_message.set(Some(
36
+
"PDS Host must start with http:// or https://".to_string(),
37
+
));
38
+
return;
39
+
}
40
+
error_message.set(None); // Clear previous error
41
+
42
+
let pds_host_val = pds_host_val.to_string();
43
+
let error_message_clone = error_message.clone();
44
+
45
+
spawn_local(async move {
46
+
match get_oauth_client()
47
+
.await
48
+
.authorize(
49
+
pds_host_val.clone(), // PDS host for authorization
50
+
AuthorizeOptions {
51
+
scopes: vec![
52
+
Scope::Known(KnownScope::Atproto),
53
+
Scope::Known(KnownScope::TransitionGeneric),
54
+
],
55
+
// The state parameter is generated and stored by the authorize method itself.
56
+
// It will be associated with the PKCE code verifier and the PDS host.
57
+
..Default::default()
58
+
},
59
+
)
60
+
.await
61
+
{
62
+
Ok(auth_url_with_state) => {
63
+
// auth_url_with_state includes the `state` query parameter generated by the SDK.
64
+
// The SDK's state_store will have stored the pkce_code_verifier and pds_host against this state.
65
+
if let Err(e) = window().location().set_href(auth_url_with_state.as_str()) {
66
+
log::error!("Failed to redirect: {:?}", e);
67
+
error_message_clone.set(Some(format!("Failed to redirect: {:?}", e)));
68
+
}
69
+
// No need to navigate with yew_router here, browser redirect handles it.
70
+
}
71
+
Err(e) => {
72
+
log::error!("Failed to get authorization URL: {:?}", e);
73
+
error_message_clone.set(Some(format!("Login failed: {:?}", e)));
74
+
}
75
+
}
76
+
});
77
+
})
78
+
};
79
+
80
+
html! {
81
+
<div class="w-full max-w-md mx-auto mt-10">
82
+
<form onsubmit={on_submit} class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
83
+
<h2 class="text-2xl font-bold mb-6 text-center">{ "Login to ATProto" }</h2>
84
+
<div class="mb-4">
85
+
<label class="block text-gray-700 text-sm font-bold mb-2" for="pds_host">
86
+
{ "PDS Host" }
87
+
</label>
88
+
<input type="text" id="pds_host" value={(*pds_host).clone()}
89
+
oninput={on_pds_host_input}
90
+
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
91
+
placeholder="e.g., https://bsky.social" />
92
+
</div>
93
+
if let Some(err) = &*error_message {
94
+
<p class="text-red-500 text-xs italic mb-4">{ err }</p>
95
+
}
96
+
<div class="flex items-center justify-between">
97
+
<button type="submit"
98
+
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
99
+
{ "Login" }
100
+
</button>
101
+
</div>
102
+
</form>
103
+
</div>
104
+
}
105
+
}