A website for the ATmosphereConf

Compare changes

Choose any two refs to compare.

Changed files
+81 -94
lexicons
src
components
lexicon
types
org
atmosphereconf
pages
-14
README.md
··· 1 - # ATmosphereConf 2 - 3 - Main website: https://atmosphereconf.org (currently redirects to our ticketing landing page) 4 - 5 - News & Updates: <https://news.atprotocol.org> (powered by Leaflet!) 6 - Save the Date with an RSVP on [Smoke Signal](https://smokesignal.events/did:plc:lehcqqkwzcwvjvw66uthu5oq/3lte3c7x43l2e) 7 - 8 - We will be building out a conference website with a handful of ATProto specific features. 9 - 10 - For open discussion, and for those who are joining as active volunteers, please visit the [community forum](https://discourse.atprotocol.community/c/atmosphereconf/25/none). 11 - 12 - ## Conference Profile 13 - For starters, we're going to have a custom conference profile. Attendees and speakers (and anyone else!) can login and create an extended profile. Inspired by [Discover Toronto](https://discover.toronto.inc/), we had an [initial discussion in the forum](https://discourse.atprotocol.community/t/conference-profiles/186) and are going to work on fleshing this out here with detailed issues. 14 - 15 1 # Astro ATProto OAuth Starter 16 2 17 3 A minimal [Astro](https://astro.build) starter template demonstrating OAuth authentication with AT Protocol (ATProto), the decentralized social networking protocol used by Bluesky and other services.
+3 -9
lexicons/profile.json
··· 14 14 "maxGraphemes": 64, 15 15 "maxLength": 640 16 16 }, 17 - "about": { 18 - "type": "string", 19 - "description": "Conference attendee extended about text", 20 - "maxGraphemes": 1024, 21 - "maxLength": 10240 22 - }, 23 - "shortbio": { 17 + "description": { 24 18 "type": "string", 25 - "description": "If you are a speaker, this is displayed as your bio. Use about for extended text", 19 + "description": "Free-form profile description text.", 26 20 "maxGraphemes": 256, 27 21 "maxLength": 2560 28 22 }, 29 23 "avatar": { 30 24 "type": "blob", 31 - "description": "Profile picture for conference attendee. Defaults to Bluesky profile pic on first login. If you are speaking, this is used as your speaker headshot", 25 + "description": "Profile picture for conference attendee", 32 26 "accept": ["image/png", "image/jpeg"], 33 27 "maxSize": 1000000 34 28 },
+19 -33
src/components/ProfileForm.astro
··· 1 1 --- 2 2 interface Props { 3 3 displayName?: string 4 - about?: string 4 + description?: string 5 5 avatar?: string 6 6 banner?: string 7 7 submitLabel?: string ··· 10 10 11 11 const { 12 12 displayName = '', 13 - about = '', 13 + description = '', 14 14 avatar = '', 15 15 banner = '', 16 16 submitLabel = 'Create Profile', ··· 26 26 > 27 27 <div class="form-control"> 28 28 <label class="label"> 29 - <span class="label-text">Attendee Name</span> 29 + <span class="label-text">Display Name</span> 30 30 <span class="label-text-alt">Max 64 characters</span> 31 31 </label> 32 32 <input 33 33 type="text" 34 34 name="displayName" 35 - placeholder="This is the display name that will be shown as your attendee name." 35 + placeholder="Enter your display name" 36 36 class="input input-bordered w-full" 37 37 value={displayName} 38 38 maxlength="64" ··· 42 42 43 43 <div class="form-control"> 44 44 <label class="label"> 45 - <span class="label-text">Attendee Image</span> 46 - <span class="label-text-alt">Your public attendee image. PNG or JPEG, max 1MB</span> 45 + <span class="label-text">Description</span> 46 + <span class="label-text-alt">Max 256 characters</span> 47 + </label> 48 + <textarea 49 + name="description" 50 + placeholder="Tell us about yourself" 51 + class="textarea textarea-bordered h-24" 52 + maxlength="256" 53 + >{description}</textarea> 54 + </div> 55 + 56 + <div class="form-control"> 57 + <label class="label"> 58 + <span class="label-text">Avatar</span> 59 + <span class="label-text-alt">PNG or JPEG, max 1MB</span> 47 60 </label> 48 61 {avatar && ( 49 62 <div class="avatar mb-2"> ··· 59 72 class="file-input file-input-bordered w-full" 60 73 /> 61 74 </div> 62 - 63 - <div class="form-control"> 64 - <label class="label"> 65 - <span class="label-text">Bio</span> 66 - <span class="label-text-alt">A short biography, displayed as speaker bios. Max 256 characters</span> 67 - </label><br /> 68 - <textarea 69 - name="shortbio" 70 - placeholder="If you are a speaker, this is displayed as your bio. Use About for extended background" 71 - class="textarea textarea-bordered h-24 w-full" 72 - maxlength="256" 73 - >{about}</textarea> 74 - </div> 75 - 76 - <div class="form-control"> 77 - <label class="label"> 78 - <span class="label-text">About</span> 79 - <span class="label-text-alt">Max 1024 characters</span> 80 - </label><br /> 81 - <textarea 82 - name="about" 83 - placeholder="Tell us about yourself" 84 - class="textarea textarea-bordered h-24 w-full" 85 - maxlength="1024" 86 - >{about}</textarea> 87 - </div> 88 - 89 75 90 76 <div class="form-control"> 91 77 <label class="label">
+46 -27
src/lexicon/lexicons.ts
··· 1 1 /** 2 2 * GENERATED CODE - DO NOT MODIFY 3 3 */ 4 - import { type LexiconDoc, Lexicons, ValidationError, type ValidationResult } from '@atproto/lexicon' 4 + import { 5 + type LexiconDoc, 6 + Lexicons, 7 + ValidationError, 8 + type ValidationResult, 9 + } from '@atproto/lexicon' 5 10 import { type $Typed, is$typed, maybe$typed } from './util.js' 6 11 7 12 export const schemaDict = { ··· 11 16 defs: { 12 17 label: { 13 18 type: 'object', 14 - description: 'Metadata tag on an atproto resource (eg, repo or record).', 19 + description: 20 + 'Metadata tag on an atproto resource (eg, repo or record).', 15 21 required: ['src', 'uri', 'val', 'cts'], 16 22 properties: { 17 23 ver: { ··· 26 32 uri: { 27 33 type: 'string', 28 34 format: 'uri', 29 - description: 'AT URI of the record, repository (account), or other resource that this label applies to.', 35 + description: 36 + 'AT URI of the record, repository (account), or other resource that this label applies to.', 30 37 }, 31 38 cid: { 32 39 type: 'string', 33 40 format: 'cid', 34 - description: "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", 41 + description: 42 + "Optionally, CID specifying the specific version of 'uri' resource this label applies to.", 35 43 }, 36 44 val: { 37 45 type: 'string', 38 46 maxLength: 128, 39 - description: 'The short string name of the value or type of this label.', 47 + description: 48 + 'The short string name of the value or type of this label.', 40 49 }, 41 50 neg: { 42 51 type: 'boolean', 43 - description: 'If true, this is a negation label, overwriting a previous label.', 52 + description: 53 + 'If true, this is a negation label, overwriting a previous label.', 44 54 }, 45 55 cts: { 46 56 type: 'string', ··· 50 60 exp: { 51 61 type: 'string', 52 62 format: 'datetime', 53 - description: 'Timestamp at which this label expires (no longer applies).', 63 + description: 64 + 'Timestamp at which this label expires (no longer applies).', 54 65 }, 55 66 sig: { 56 67 type: 'bytes', ··· 60 71 }, 61 72 selfLabels: { 62 73 type: 'object', 63 - description: 'Metadata tags on an atproto record, published by the author within the record.', 74 + description: 75 + 'Metadata tags on an atproto record, published by the author within the record.', 64 76 required: ['values'], 65 77 properties: { 66 78 values: { ··· 82 94 val: { 83 95 type: 'string', 84 96 maxLength: 128, 85 - description: 'The short string name of the value or type of this label.', 97 + description: 98 + 'The short string name of the value or type of this label.', 86 99 }, 87 100 }, 88 101 }, 89 102 labelValueDefinition: { 90 103 type: 'object', 91 - description: 'Declares a label value and its expected interpretations and behaviors.', 104 + description: 105 + 'Declares a label value and its expected interpretations and behaviors.', 92 106 required: ['identifier', 'severity', 'blurs', 'locales'], 93 107 properties: { 94 108 identifier: { ··· 118 132 }, 119 133 adultOnly: { 120 134 type: 'boolean', 121 - description: 'Does the user need to have adult content enabled in order to configure this label?', 135 + description: 136 + 'Does the user need to have adult content enabled in order to configure this label?', 122 137 }, 123 138 locales: { 124 139 type: 'array', ··· 131 146 }, 132 147 labelValueDefinitionStrings: { 133 148 type: 'object', 134 - description: 'Strings which describe the label in the UI, localized into a specific language.', 149 + description: 150 + 'Strings which describe the label in the UI, localized into a specific language.', 135 151 required: ['lang', 'name', 'description'], 136 152 properties: { 137 153 lang: { 138 154 type: 'string', 139 - description: 'The code of the language these strings are written in.', 155 + description: 156 + 'The code of the language these strings are written in.', 140 157 format: 'language', 141 158 }, 142 159 name: { ··· 147 164 }, 148 165 description: { 149 166 type: 'string', 150 - description: 'A longer description of what the label means and why it might be applied.', 167 + description: 168 + 'A longer description of what the label means and why it might be applied.', 151 169 maxGraphemes: 10000, 152 170 maxLength: 100000, 153 171 }, ··· 187 205 maxGraphemes: 64, 188 206 maxLength: 640, 189 207 }, 190 - about: { 208 + description: { 191 209 type: 'string', 192 - description: 'Conference attendee extended about text', 193 - maxGraphemes: 512, 194 - maxLength: 5120, 195 - }, 196 - shortbio: { 197 - type: 'string', 198 - description: 'If you are a speaker, this is displayed as your bio. Use about for extended text', 210 + description: 'Free-form profile description text.', 199 211 maxGraphemes: 256, 200 212 maxLength: 2560, 201 213 }, 202 214 avatar: { 203 215 type: 'blob', 204 - description: 205 - 'Profile picture for conference attendee. Defaults to Bluesky profile pic on first login. If you are speaking, this is used as your speaker headshot', 216 + description: 'Profile picture for conference attendee', 206 217 accept: ['image/png', 'image/jpeg'], 207 218 maxSize: 1000000, 208 219 }, 209 220 banner: { 210 221 type: 'blob', 211 - description: 'Larger horizontal image to display behind profile view.', 222 + description: 223 + 'Larger horizontal image to display behind profile view.', 212 224 accept: ['image/png', 'image/jpeg'], 213 225 maxSize: 1000000, 214 226 }, ··· 263 275 hash: string, 264 276 requiredType?: false, 265 277 ): ValidationResult<T> 266 - export function validate(v: unknown, id: string, hash: string, requiredType?: boolean): ValidationResult { 278 + export function validate( 279 + v: unknown, 280 + id: string, 281 + hash: string, 282 + requiredType?: boolean, 283 + ): ValidationResult { 267 284 return (requiredType ? is$typed : maybe$typed)(v, id, hash) 268 285 ? lexicons.validate(`${id}#${hash}`, v) 269 286 : { 270 287 success: false, 271 - error: new ValidationError(`Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`), 288 + error: new ValidationError( 289 + `Must be an object with "${hash === 'main' ? id : `${id}#${hash}`}" $type property`, 290 + ), 272 291 } 273 292 } 274 293
+6 -4
src/lexicon/types/org/atmosphereconf/profile.ts
··· 14 14 export interface Main { 15 15 $type: 'org.atmosphereconf.profile' 16 16 displayName?: string 17 - /** Bio for speakers */ 18 - shortbio?: string 19 17 /** Free-form profile description text. */ 20 - about?: string 18 + description?: string 21 19 /** Profile picture for conference attendee */ 22 20 avatar?: BlobRef 23 21 /** Larger horizontal image to display behind profile view. */ ··· 37 35 return validate<Main & V>(v, id, hashMain, true) 38 36 } 39 37 40 - export { type Main as Record, isMain as isRecord, validateMain as validateRecord } 38 + export { 39 + type Main as Record, 40 + isMain as isRecord, 41 + validateMain as validateRecord, 42 + }
+2 -2
src/pages/index.astro
··· 34 34 const displayName = profile?.displayName || agent?.assertDid || 'User' 35 35 const handle = profile?.handle || agent?.assertDid || '' 36 36 const avatar = profile?.avatar 37 - const shortbio = profile?.shortbio 37 + const description = profile?.description 38 38 --- 39 39 40 40 <html lang="en" data-theme="dracula"> ··· 64 64 )} 65 65 <p class="text-lg font-semibold">{displayName}</p> 66 66 <p class="text-sm opacity-70">{handle}</p> 67 - {shortbio && <p class="text-sm mt-2 opacity-80">{shortbio}</p>} 67 + {description && <p class="text-sm mt-2 opacity-80">{description}</p>} 68 68 </div> 69 69 <div class="space-y-2 w-full"> 70 70 <a href={`/profile/${handle}`} class="btn btn-primary w-full">
+3 -3
src/pages/profile/[handle].astro
··· 80 80 } 81 81 82 82 const displayName = conferenceProfile?.displayName || profile?.displayName || handle 83 - const shortbio = conferenceProfile?.shortbio || profile?.description || '' 83 + const description = conferenceProfile?.description || profile?.description || '' 84 84 85 85 // Handle both blob refs and direct URLs 86 86 let avatar = '' ··· 159 159 )} 160 160 </div> 161 161 162 - {shortbio && ( 163 - <p class="mt-4 text-base whitespace-pre-wrap">{shortbio}</p> 162 + {description && ( 163 + <p class="mt-4 text-base whitespace-pre-wrap">{description}</p> 164 164 )} 165 165 166 166 <div class="mt-4">
+2 -2
src/pages/profile/create.astro
··· 43 43 } 44 44 45 45 const displayName = existingProfile?.displayName || '' 46 - const about = existingProfile?.about || '' 46 + const description = existingProfile?.description || '' 47 47 --- 48 48 49 49 <html lang="en" data-theme="dracula"> ··· 71 71 72 72 <ProfileForm 73 73 displayName={displayName} 74 - about={about} 74 + description={description} 75 75 submitLabel={existingProfile ? 'Update Profile' : 'Create Profile'} 76 76 /> 77 77