the statusphere demo reworked into a vite/react app in a monorepo

A few tightening edits

+22 -38
+22 -38
TUTORIAL.md
··· 119 120 ## Step 3. Fetching the user's profile 121 122 - Why don't we learn something about our user? Let's start by getting the [Agent](#todo) object. The [Agent](#todo) is the client to the user's `at://` repo server. 123 - 124 - ```typescript 125 - /** src/routes.ts **/ 126 - async function getSessionAgent( 127 - req: IncomingMessage, 128 - res: ServerResponse<IncomingMessage>, 129 - ctx: AppContext 130 - ) { 131 - // Fetch the session from their cookie 132 - const session = await getIronSession(req, res) 133 - if (!session.did) return null 134 - 135 - // "Restore" the agent for the user 136 - try { 137 - return await ctx.oauthClient.restore(session.did) 138 - } catch(err) { 139 - ctx.logger.warn({ err }, 'oauth restore failed') 140 - await session.destroy() 141 - return null 142 - } 143 - } 144 - ``` 145 - 146 - Users publish JSON records on their `at://` repos. In [Bluesky](https://bsky.app), they publish a "profile" record which looks like this: 147 148 ```typescript 149 interface ProfileRecord { ··· 156 } 157 ``` 158 159 - We're going to use the [Agent](#todo) to fetch this record to include in our app. 160 161 ```typescript 162 await agent.getRecord({ ··· 168 169 When asking for a record, we provide three pieces of information. 170 171 - - The [DID](#todo) which identifies the user, 172 - - The collection name, and 173 - - The record key 174 175 We'll explain the collection name shortly. Record keys are strings with [some limitations](https://atproto.com/specs/record-key#record-key-syntax) and a couple of common patterns. The `"self"` pattern is used when a collection is expected to only contain one record which describes the user. 176 ··· 194 const { data: profileRecord } = await agent.getRecord({ 195 repo: agent.accountDid, // our user's repo 196 collection: 'app.bsky.actor.profile', // the bluesky profile record type 197 - rkey: 'self', // the record's name 198 }) 199 200 // Serve the logged-in view ··· 206 ``` 207 208 With that data, we can give a nice personalized welcome banner for our user: 209 210 ```html 211 <!-- pages/home.ts --> ··· 228 </div>`} 229 </div> 230 ``` 231 - 232 - ![A screenshot of the banner image](./docs/app-banner.png) 233 - 234 - You can examine this record directly using [atproto-browser.vercel.app](https://atproto-browser.vercel.app). For instance, [this is the profile record for @bsky.app](https://atproto-browser.vercel.app/at?u=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.actor.profile/self). 235 236 ## Step 4. Reading & writing records 237 ··· 325 326 ## Step 5. Creating a custom "status" schema 327 328 - The collections are typed, meaning that they have a defined schema. The `app.bsky.actor.profile` type definition [can be found here](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/profile.json). 329 330 Anybody can create a new schema using the [Lexicon](#todo) language, which is very similar to [JSON-Schema](#todo). The schemas use [reverse-DNS IDs](#todo) which indicate ownership, but for this demo app we're going to use `com.example` which is safe for non-production software. 331 ··· 511 512 ![A diagram of the flow of information](./docs/diagram-info-flow.png) 513 514 - Why read from the event log? Because there are other apps in the network that will write the records we're interested in. By subscribing to the event log, we ensure that we catch all the data we're interested in -- including data published by other apps. 515 516 ## Step 7. Listing the latest statuses 517 ··· 567 568 ## Step 8. Optimistic updates 569 570 - As a final optimization, let's introduce "optimistic updates." Remember the information flow loop with the repo write and the event log? Since we're updating our users' repos locally, we can short-circuit that flow to our own database: 571 572 ![A diagram illustrating optimistic updates](./docs/diagram-optimistic-update.png) 573 ··· 633 - Design the [Lexicon](#) schemas for the records you'll publish into the Atmosphere. 634 - Create a database for aggregating the records into useful views. 635 - Build your application to write the records on your users' repos. 636 - - Listen to the firehose to hydrate your aggregated database. 637 638 Remember this flow of information throughout: 639
··· 119 120 ## Step 3. Fetching the user's profile 121 122 + Why don't we learn something about our user? In [Bluesky](https://bsky.app), users publish a "profile" record which looks like this: 123 124 ```typescript 125 interface ProfileRecord { ··· 132 } 133 ``` 134 135 + You can examine this record directly using [atproto-browser.vercel.app](https://atproto-browser.vercel.app). For instance, [this is the profile record for @bsky.app](https://atproto-browser.vercel.app/at?u=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.actor.profile/self). 136 + 137 + We're going to use the [Agent](#todo) associated with the user's OAuth session to fetch this record. 138 139 ```typescript 140 await agent.getRecord({ ··· 146 147 When asking for a record, we provide three pieces of information. 148 149 + - **repo** The [DID](#todo) which identifies the user, 150 + - **collection** The collection name, and 151 + - **rkey** The record key 152 153 We'll explain the collection name shortly. Record keys are strings with [some limitations](https://atproto.com/specs/record-key#record-key-syntax) and a couple of common patterns. The `"self"` pattern is used when a collection is expected to only contain one record which describes the user. 154 ··· 172 const { data: profileRecord } = await agent.getRecord({ 173 repo: agent.accountDid, // our user's repo 174 collection: 'app.bsky.actor.profile', // the bluesky profile record type 175 + rkey: 'self', // the record's key 176 }) 177 178 // Serve the logged-in view ··· 184 ``` 185 186 With that data, we can give a nice personalized welcome banner for our user: 187 + 188 + ![A screenshot of the banner image](./docs/app-banner.png) 189 190 ```html 191 <!-- pages/home.ts --> ··· 208 </div>`} 209 </div> 210 ``` 211 212 ## Step 4. Reading & writing records 213 ··· 301 302 ## Step 5. Creating a custom "status" schema 303 304 + Repo collections are typed, meaning that they have a defined schema. The `app.bsky.actor.profile` type definition [can be found here](https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/profile.json). 305 306 Anybody can create a new schema using the [Lexicon](#todo) language, which is very similar to [JSON-Schema](#todo). The schemas use [reverse-DNS IDs](#todo) which indicate ownership, but for this demo app we're going to use `com.example` which is safe for non-production software. 307 ··· 487 488 ![A diagram of the flow of information](./docs/diagram-info-flow.png) 489 490 + Applications write to the repo. The write events are then emitted on the firehose where they're caught by the apps and ingested into their databases. 491 + 492 + Why sync from the event log like this? Because there are other apps in the network that will write the records we're interested in. By subscribing to the event log, we ensure that we catch all the data we're interested in &mdash; including data published by other apps! 493 494 ## Step 7. Listing the latest statuses 495 ··· 545 546 ## Step 8. Optimistic updates 547 548 + As a final optimization, let's introduce "optimistic updates." 549 + 550 + Remember the information flow loop with the repo write and the event log? 551 + 552 + ![A diagram of the flow of information](./docs/diagram-info-flow.png) 553 + 554 + Since we're updating our users' repos locally, we can short-circuit that flow to our own database: 555 556 ![A diagram illustrating optimistic updates](./docs/diagram-optimistic-update.png) 557 ··· 617 - Design the [Lexicon](#) schemas for the records you'll publish into the Atmosphere. 618 - Create a database for aggregating the records into useful views. 619 - Build your application to write the records on your users' repos. 620 + - Listen to the firehose to aggregate data across the network. 621 622 Remember this flow of information throughout: 623