Monorepo for Tangled tangled.org

appview/pages: upload and render avatar

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>

anirudh.fi fa732f2b 43da38df

verified
Changed files
+92 -8
appview
pages
+22
appview/pages/funcmap.go
··· 360 "fullAvatar": func(handle string) string { 361 return p.AvatarUrl(handle, "") 362 }, 363 "langColor": enry.GetColor, 364 "layoutSide": func() string { 365 return "col-span-1 md:col-span-2 lg:col-span-3"
··· 360 "fullAvatar": func(handle string) string { 361 return p.AvatarUrl(handle, "") 362 }, 363 + "placeholderAvatar": func(size string) template.HTML { 364 + sizeClass := "size-6" 365 + iconSize := "size-4" 366 + if size == "tiny" { 367 + sizeClass = "size-6" 368 + iconSize = "size-4" 369 + } else if size == "small" { 370 + sizeClass = "size-8" 371 + iconSize = "size-5" 372 + } else { 373 + sizeClass = "size-12" 374 + iconSize = "size-8" 375 + } 376 + icon, _ := p.icon("user-round", []string{iconSize, "text-gray-400", "dark:text-gray-500"}) 377 + return template.HTML(fmt.Sprintf(`<div class="%s rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center flex-shrink-0">%s</div>`, sizeClass, icon)) 378 + }, 379 + "profileAvatarUrl": func(profile *models.Profile, size string) string { 380 + if profile != nil { 381 + return p.AvatarUrl(profile.Did, size) 382 + } 383 + return "" 384 + }, 385 "langColor": enry.GetColor, 386 "layoutSide": func() string { 387 return "col-span-1 md:col-span-2 lg:col-span-3"
+1 -1
appview/pages/templates/layouts/profilebase.html
··· 2 3 {{ define "extrameta" }} 4 {{ $handle := resolve .Card.UserDid }} 5 - {{ $avatarUrl := fullAvatar $handle }} 6 <meta property="og:title" content="{{ $handle }}" /> 7 <meta property="og:type" content="profile" /> 8 <meta property="og:url" content="https://tangled.org/{{ $handle }}?tab={{ .Active }}" />
··· 2 3 {{ define "extrameta" }} 4 {{ $handle := resolve .Card.UserDid }} 5 + {{ $avatarUrl := profileAvatarUrl .Card.Profile "" }} 6 <meta property="og:title" content="{{ $handle }}" /> 7 <meta property="og:type" content="profile" /> 8 <meta property="og:url" content="https://tangled.org/{{ $handle }}?tab={{ .Active }}" />
+44
appview/pages/templates/user/fragments/editAvatar.html
···
··· 1 + {{ define "user/fragments/editAvatar" }} 2 + <form 3 + hx-post="/profile/avatar" 4 + hx-encoding="multipart/form-data" 5 + hx-indicator="#spinner" 6 + hx-swap="none" 7 + class="flex flex-col gap-2"> 8 + <label for="avatar-file" class="uppercase p-0"> 9 + Upload avatar 10 + </label> 11 + <p class="text-sm text-gray-500 dark:text-gray-400">Select an image (PNG or JPEG, max 1MB)</p> 12 + <input 13 + type="file" 14 + id="avatar-file" 15 + name="avatar" 16 + accept="image/png,image/jpeg" 17 + required 18 + class="block w-full text-sm text-gray-500 dark:text-gray-400 19 + file:mr-4 file:py-2 file:px-4 20 + file:rounded file:border-0 21 + file:text-sm file:font-semibold 22 + file:bg-gray-100 file:text-gray-700 23 + dark:file:bg-gray-700 dark:file:text-gray-300 24 + hover:file:bg-gray-200 dark:hover:file:bg-gray-600" /> 25 + <div class="flex gap-2 pt-2"> 26 + <button 27 + id="cancel-avatar-btn" 28 + type="button" 29 + popovertarget="avatar-upload-modal" 30 + popovertargetaction="hide" 31 + class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 32 + {{ i "x" "size-4" }} 33 + cancel 34 + </button> 35 + <button type="submit" class="btn w-1/2 flex items-center"> 36 + <span class="inline-flex gap-2 items-center">{{ i "upload" "size-4" }} upload</span> 37 + <span id="spinner" class="group"> 38 + {{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 39 + </span> 40 + </button> 41 + </div> 42 + <div id="avatar-error" class="text-red-500 dark:text-red-400"></div> 43 + </form> 44 + {{ end }}
+17 -3
appview/pages/templates/user/fragments/profileCard.html
··· 3 <div class="grid grid-cols-3 md:grid-cols-1 gap-1 items-center"> 4 <div id="avatar" class="col-span-1 flex justify-center items-center"> 5 <div class="w-3/4 aspect-square relative"> 6 - <img class="absolute inset-0 w-full h-full object-cover rounded-full p-2" src="{{ fullAvatar .UserDid }}" /> 7 </div> 8 </div> 9 <div class="col-span-2"> 10 <div class="flex items-center flex-row flex-nowrap gap-2"> ··· 36 {{ block "followerFollowing" (list $ $userIdent) }} {{ end }} 37 </div> 38 39 - <div class="flex flex-col gap-2 mb-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full"> 40 {{ if .Location }} 41 <div class="flex items-center gap-2"> 42 <span class="flex-shrink-0">{{ i "map-pin" "size-4" }}</span> ··· 111 </div> 112 {{ end }} 113 {{ end }} 114 -
··· 3 <div class="grid grid-cols-3 md:grid-cols-1 gap-1 items-center"> 4 <div id="avatar" class="col-span-1 flex justify-center items-center"> 5 <div class="w-3/4 aspect-square relative"> 6 + <img class="absolute inset-0 w-full h-full object-cover rounded-full p-2" src="{{ profileAvatarUrl .Profile "" }}" /> 7 + {{ if eq .FollowStatus.String "IsSelf" }} 8 + <button 9 + class="absolute bottom-2 right-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-full p-2 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" 10 + popovertarget="avatar-upload-modal" 11 + popovertargetaction="toggle" 12 + title="Upload avatar"> 13 + {{ i "camera" "w-4 h-4" }} 14 + </button> 15 + {{ end }} 16 </div> 17 + </div> 18 + <div 19 + id="avatar-upload-modal" 20 + popover 21 + class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50"> 22 + {{ template "user/fragments/editAvatar" . }} 23 </div> 24 <div class="col-span-2"> 25 <div class="flex items-center flex-row flex-nowrap gap-2"> ··· 51 {{ block "followerFollowing" (list $ $userIdent) }} {{ end }} 52 </div> 53 54 + <div class="flex flex-col gap-2 mb-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full"> 55 {{ if .Location }} 56 <div class="flex items-center gap-2"> 57 <span class="flex-shrink-0">{{ i "map-pin" "size-4" }}</span> ··· 126 </div> 127 {{ end }} 128 {{ end }}
+4 -2
appview/pages/templates/user/settings/emails.html
··· 62 hx-swap="none" 63 class="flex flex-col gap-2" 64 > 65 - <p class="uppercase p-0">ADD EMAIL</p> 66 <p class="text-sm text-gray-500 dark:text-gray-400">Commits using this email will be associated with your profile.</p> 67 <input 68 type="email" ··· 91 <div id="settings-emails-error" class="text-red-500 dark:text-red-400"></div> 92 <div id="settings-emails-success" class="text-green-500 dark:text-green-400"></div> 93 </form> 94 - {{ end }}
··· 62 hx-swap="none" 63 class="flex flex-col gap-2" 64 > 65 + <label for="email-address" class="uppercase p-0"> 66 + add email 67 + </label> 68 <p class="text-sm text-gray-500 dark:text-gray-400">Commits using this email will be associated with your profile.</p> 69 <input 70 type="email" ··· 93 <div id="settings-emails-error" class="text-red-500 dark:text-red-400"></div> 94 <div id="settings-emails-success" class="text-green-500 dark:text-green-400"></div> 95 </form> 96 + {{ end }}
+4 -2
appview/pages/templates/user/settings/keys.html
··· 21 <div class="col-span-1 md:col-span-2"> 22 <h2 class="text-sm pb-2 uppercase font-bold">SSH Keys</h2> 23 <p class="text-gray-500 dark:text-gray-400"> 24 - SSH public keys added here will be broadcasted to knots that you are a member of, 25 allowing you to push to repositories there. 26 </p> 27 </div> ··· 63 hx-swap="none" 64 class="flex flex-col gap-2" 65 > 66 - <p class="uppercase p-0">ADD SSH KEY</p> 67 <p class="text-sm text-gray-500 dark:text-gray-400">SSH keys allow you to push to repositories in knots you're a member of.</p> 68 <input 69 type="text"
··· 21 <div class="col-span-1 md:col-span-2"> 22 <h2 class="text-sm pb-2 uppercase font-bold">SSH Keys</h2> 23 <p class="text-gray-500 dark:text-gray-400"> 24 + SSH public keys added here will be broadcasted to knots that you are a member of, 25 allowing you to push to repositories there. 26 </p> 27 </div> ··· 63 hx-swap="none" 64 class="flex flex-col gap-2" 65 > 66 + <label for="key-name" class="uppercase p-0"> 67 + add ssh key 68 + </label> 69 <p class="text-sm text-gray-500 dark:text-gray-400">SSH keys allow you to push to repositories in knots you're a member of.</p> 70 <input 71 type="text"