pages: support MultiAccountUser in templates and params

Changed files
+180 -77
appview
pages
templates
layouts
fragments
user
+10
appview/pages/funcmap.go
··· 28 28 emoji "github.com/yuin/goldmark-emoji" 29 29 "tangled.org/core/appview/filetree" 30 30 "tangled.org/core/appview/models" 31 + "tangled.org/core/appview/oauth" 31 32 "tangled.org/core/appview/pages/markup" 32 33 "tangled.org/core/crypto" 33 34 ) ··· 384 385 return "error" 385 386 } 386 387 return fp 388 + }, 389 + "otherAccounts": func(activeDid string, accounts []oauth.AccountInfo) []oauth.AccountInfo { 390 + result := make([]oauth.AccountInfo, 0, len(accounts)) 391 + for _, acc := range accounts { 392 + if acc.Did != activeDid { 393 + result = append(result, acc) 394 + } 395 + } 396 + return result 387 397 }, 388 398 } 389 399 }
+68 -66
appview/pages/pages.go
··· 215 215 } 216 216 217 217 type LoginParams struct { 218 - ReturnUrl string 219 - ErrorCode string 218 + ReturnUrl string 219 + ErrorCode string 220 + AddAccount bool 221 + LoggedInUser *oauth.MultiAccountUser 220 222 } 221 223 222 224 func (p *Pages) Login(w io.Writer, params LoginParams) error { ··· 236 238 } 237 239 238 240 type TermsOfServiceParams struct { 239 - LoggedInUser *oauth.User 241 + LoggedInUser *oauth.MultiAccountUser 240 242 Content template.HTML 241 243 } 242 244 ··· 264 266 } 265 267 266 268 type PrivacyPolicyParams struct { 267 - LoggedInUser *oauth.User 269 + LoggedInUser *oauth.MultiAccountUser 268 270 Content template.HTML 269 271 } 270 272 ··· 292 294 } 293 295 294 296 type BrandParams struct { 295 - LoggedInUser *oauth.User 297 + LoggedInUser *oauth.MultiAccountUser 296 298 } 297 299 298 300 func (p *Pages) Brand(w io.Writer, params BrandParams) error { ··· 300 302 } 301 303 302 304 type TimelineParams struct { 303 - LoggedInUser *oauth.User 305 + LoggedInUser *oauth.MultiAccountUser 304 306 Timeline []models.TimelineEvent 305 307 Repos []models.Repo 306 308 GfiLabel *models.LabelDefinition ··· 311 313 } 312 314 313 315 type GoodFirstIssuesParams struct { 314 - LoggedInUser *oauth.User 316 + LoggedInUser *oauth.MultiAccountUser 315 317 Issues []models.Issue 316 318 RepoGroups []*models.RepoGroup 317 319 LabelDefs map[string]*models.LabelDefinition ··· 324 326 } 325 327 326 328 type UserProfileSettingsParams struct { 327 - LoggedInUser *oauth.User 329 + LoggedInUser *oauth.MultiAccountUser 328 330 Tabs []map[string]any 329 331 Tab string 330 332 } ··· 334 336 } 335 337 336 338 type NotificationsParams struct { 337 - LoggedInUser *oauth.User 339 + LoggedInUser *oauth.MultiAccountUser 338 340 Notifications []*models.NotificationWithEntity 339 341 UnreadCount int 340 342 Page pagination.Page ··· 362 364 } 363 365 364 366 type UserKeysSettingsParams struct { 365 - LoggedInUser *oauth.User 367 + LoggedInUser *oauth.MultiAccountUser 366 368 PubKeys []models.PublicKey 367 369 Tabs []map[string]any 368 370 Tab string ··· 373 375 } 374 376 375 377 type UserEmailsSettingsParams struct { 376 - LoggedInUser *oauth.User 378 + LoggedInUser *oauth.MultiAccountUser 377 379 Emails []models.Email 378 380 Tabs []map[string]any 379 381 Tab string ··· 384 386 } 385 387 386 388 type UserNotificationSettingsParams struct { 387 - LoggedInUser *oauth.User 389 + LoggedInUser *oauth.MultiAccountUser 388 390 Preferences *models.NotificationPreferences 389 391 Tabs []map[string]any 390 392 Tab string ··· 404 406 } 405 407 406 408 type KnotsParams struct { 407 - LoggedInUser *oauth.User 409 + LoggedInUser *oauth.MultiAccountUser 408 410 Registrations []models.Registration 409 411 Tabs []map[string]any 410 412 Tab string ··· 415 417 } 416 418 417 419 type KnotParams struct { 418 - LoggedInUser *oauth.User 420 + LoggedInUser *oauth.MultiAccountUser 419 421 Registration *models.Registration 420 422 Members []string 421 423 Repos map[string][]models.Repo ··· 437 439 } 438 440 439 441 type SpindlesParams struct { 440 - LoggedInUser *oauth.User 442 + LoggedInUser *oauth.MultiAccountUser 441 443 Spindles []models.Spindle 442 444 Tabs []map[string]any 443 445 Tab string ··· 458 460 } 459 461 460 462 type SpindleDashboardParams struct { 461 - LoggedInUser *oauth.User 463 + LoggedInUser *oauth.MultiAccountUser 462 464 Spindle models.Spindle 463 465 Members []string 464 466 Repos map[string][]models.Repo ··· 471 473 } 472 474 473 475 type NewRepoParams struct { 474 - LoggedInUser *oauth.User 476 + LoggedInUser *oauth.MultiAccountUser 475 477 Knots []string 476 478 } 477 479 ··· 480 482 } 481 483 482 484 type ForkRepoParams struct { 483 - LoggedInUser *oauth.User 485 + LoggedInUser *oauth.MultiAccountUser 484 486 Knots []string 485 487 RepoInfo repoinfo.RepoInfo 486 488 } ··· 518 520 } 519 521 520 522 type ProfileOverviewParams struct { 521 - LoggedInUser *oauth.User 523 + LoggedInUser *oauth.MultiAccountUser 522 524 Repos []models.Repo 523 525 CollaboratingRepos []models.Repo 524 526 ProfileTimeline *models.ProfileTimeline ··· 532 534 } 533 535 534 536 type ProfileReposParams struct { 535 - LoggedInUser *oauth.User 537 + LoggedInUser *oauth.MultiAccountUser 536 538 Repos []models.Repo 537 539 Card *ProfileCard 538 540 Active string ··· 544 546 } 545 547 546 548 type ProfileStarredParams struct { 547 - LoggedInUser *oauth.User 549 + LoggedInUser *oauth.MultiAccountUser 548 550 Repos []models.Repo 549 551 Card *ProfileCard 550 552 Active string ··· 556 558 } 557 559 558 560 type ProfileStringsParams struct { 559 - LoggedInUser *oauth.User 561 + LoggedInUser *oauth.MultiAccountUser 560 562 Strings []models.String 561 563 Card *ProfileCard 562 564 Active string ··· 569 571 570 572 type FollowCard struct { 571 573 UserDid string 572 - LoggedInUser *oauth.User 574 + LoggedInUser *oauth.MultiAccountUser 573 575 FollowStatus models.FollowStatus 574 576 FollowersCount int64 575 577 FollowingCount int64 ··· 577 579 } 578 580 579 581 type ProfileFollowersParams struct { 580 - LoggedInUser *oauth.User 582 + LoggedInUser *oauth.MultiAccountUser 581 583 Followers []FollowCard 582 584 Card *ProfileCard 583 585 Active string ··· 589 591 } 590 592 591 593 type ProfileFollowingParams struct { 592 - LoggedInUser *oauth.User 594 + LoggedInUser *oauth.MultiAccountUser 593 595 Following []FollowCard 594 596 Card *ProfileCard 595 597 Active string ··· 610 612 } 611 613 612 614 type EditBioParams struct { 613 - LoggedInUser *oauth.User 615 + LoggedInUser *oauth.MultiAccountUser 614 616 Profile *models.Profile 615 617 } 616 618 ··· 619 621 } 620 622 621 623 type EditPinsParams struct { 622 - LoggedInUser *oauth.User 624 + LoggedInUser *oauth.MultiAccountUser 623 625 Profile *models.Profile 624 626 AllRepos []PinnedRepo 625 627 } ··· 644 646 } 645 647 646 648 type RepoIndexParams struct { 647 - LoggedInUser *oauth.User 649 + LoggedInUser *oauth.MultiAccountUser 648 650 RepoInfo repoinfo.RepoInfo 649 651 Active string 650 652 TagMap map[string][]string ··· 693 695 } 694 696 695 697 type RepoLogParams struct { 696 - LoggedInUser *oauth.User 698 + LoggedInUser *oauth.MultiAccountUser 697 699 RepoInfo repoinfo.RepoInfo 698 700 TagMap map[string][]string 699 701 Active string ··· 710 712 } 711 713 712 714 type RepoCommitParams struct { 713 - LoggedInUser *oauth.User 715 + LoggedInUser *oauth.MultiAccountUser 714 716 RepoInfo repoinfo.RepoInfo 715 717 Active string 716 718 EmailToDid map[string]string ··· 729 731 } 730 732 731 733 type RepoTreeParams struct { 732 - LoggedInUser *oauth.User 734 + LoggedInUser *oauth.MultiAccountUser 733 735 RepoInfo repoinfo.RepoInfo 734 736 Active string 735 737 BreadCrumbs [][]string ··· 784 786 } 785 787 786 788 type RepoBranchesParams struct { 787 - LoggedInUser *oauth.User 789 + LoggedInUser *oauth.MultiAccountUser 788 790 RepoInfo repoinfo.RepoInfo 789 791 Active string 790 792 types.RepoBranchesResponse ··· 796 798 } 797 799 798 800 type RepoTagsParams struct { 799 - LoggedInUser *oauth.User 801 + LoggedInUser *oauth.MultiAccountUser 800 802 RepoInfo repoinfo.RepoInfo 801 803 Active string 802 804 types.RepoTagsResponse ··· 810 812 } 811 813 812 814 type RepoArtifactParams struct { 813 - LoggedInUser *oauth.User 815 + LoggedInUser *oauth.MultiAccountUser 814 816 RepoInfo repoinfo.RepoInfo 815 817 Artifact models.Artifact 816 818 } ··· 820 822 } 821 823 822 824 type RepoBlobParams struct { 823 - LoggedInUser *oauth.User 825 + LoggedInUser *oauth.MultiAccountUser 824 826 RepoInfo repoinfo.RepoInfo 825 827 Active string 826 828 BreadCrumbs [][]string ··· 844 846 } 845 847 846 848 type RepoSettingsParams struct { 847 - LoggedInUser *oauth.User 849 + LoggedInUser *oauth.MultiAccountUser 848 850 RepoInfo repoinfo.RepoInfo 849 851 Collaborators []Collaborator 850 852 Active string ··· 863 865 } 864 866 865 867 type RepoGeneralSettingsParams struct { 866 - LoggedInUser *oauth.User 868 + LoggedInUser *oauth.MultiAccountUser 867 869 RepoInfo repoinfo.RepoInfo 868 870 Labels []models.LabelDefinition 869 871 DefaultLabels []models.LabelDefinition ··· 881 883 } 882 884 883 885 type RepoAccessSettingsParams struct { 884 - LoggedInUser *oauth.User 886 + LoggedInUser *oauth.MultiAccountUser 885 887 RepoInfo repoinfo.RepoInfo 886 888 Active string 887 889 Tabs []map[string]any ··· 895 897 } 896 898 897 899 type RepoPipelineSettingsParams struct { 898 - LoggedInUser *oauth.User 900 + LoggedInUser *oauth.MultiAccountUser 899 901 RepoInfo repoinfo.RepoInfo 900 902 Active string 901 903 Tabs []map[string]any ··· 911 913 } 912 914 913 915 type RepoIssuesParams struct { 914 - LoggedInUser *oauth.User 916 + LoggedInUser *oauth.MultiAccountUser 915 917 RepoInfo repoinfo.RepoInfo 916 918 Active string 917 919 Issues []models.Issue ··· 928 930 } 929 931 930 932 type RepoSingleIssueParams struct { 931 - LoggedInUser *oauth.User 933 + LoggedInUser *oauth.MultiAccountUser 932 934 RepoInfo repoinfo.RepoInfo 933 935 Active string 934 936 Issue *models.Issue ··· 947 949 } 948 950 949 951 type EditIssueParams struct { 950 - LoggedInUser *oauth.User 952 + LoggedInUser *oauth.MultiAccountUser 951 953 RepoInfo repoinfo.RepoInfo 952 954 Issue *models.Issue 953 955 Action string ··· 971 973 } 972 974 973 975 type RepoNewIssueParams struct { 974 - LoggedInUser *oauth.User 976 + LoggedInUser *oauth.MultiAccountUser 975 977 RepoInfo repoinfo.RepoInfo 976 978 Issue *models.Issue // existing issue if any -- passed when editing 977 979 Active string ··· 985 987 } 986 988 987 989 type EditIssueCommentParams struct { 988 - LoggedInUser *oauth.User 990 + LoggedInUser *oauth.MultiAccountUser 989 991 RepoInfo repoinfo.RepoInfo 990 992 Issue *models.Issue 991 993 Comment *models.IssueComment ··· 996 998 } 997 999 998 1000 type ReplyIssueCommentPlaceholderParams struct { 999 - LoggedInUser *oauth.User 1001 + LoggedInUser *oauth.MultiAccountUser 1000 1002 RepoInfo repoinfo.RepoInfo 1001 1003 Issue *models.Issue 1002 1004 Comment *models.IssueComment ··· 1007 1009 } 1008 1010 1009 1011 type ReplyIssueCommentParams struct { 1010 - LoggedInUser *oauth.User 1012 + LoggedInUser *oauth.MultiAccountUser 1011 1013 RepoInfo repoinfo.RepoInfo 1012 1014 Issue *models.Issue 1013 1015 Comment *models.IssueComment ··· 1018 1020 } 1019 1021 1020 1022 type IssueCommentBodyParams struct { 1021 - LoggedInUser *oauth.User 1023 + LoggedInUser *oauth.MultiAccountUser 1022 1024 RepoInfo repoinfo.RepoInfo 1023 1025 Issue *models.Issue 1024 1026 Comment *models.IssueComment ··· 1029 1031 } 1030 1032 1031 1033 type RepoNewPullParams struct { 1032 - LoggedInUser *oauth.User 1034 + LoggedInUser *oauth.MultiAccountUser 1033 1035 RepoInfo repoinfo.RepoInfo 1034 1036 Branches []types.Branch 1035 1037 Strategy string ··· 1046 1048 } 1047 1049 1048 1050 type RepoPullsParams struct { 1049 - LoggedInUser *oauth.User 1051 + LoggedInUser *oauth.MultiAccountUser 1050 1052 RepoInfo repoinfo.RepoInfo 1051 1053 Pulls []*models.Pull 1052 1054 Active string ··· 1081 1083 } 1082 1084 1083 1085 type RepoSinglePullParams struct { 1084 - LoggedInUser *oauth.User 1086 + LoggedInUser *oauth.MultiAccountUser 1085 1087 RepoInfo repoinfo.RepoInfo 1086 1088 Active string 1087 1089 Pull *models.Pull ··· 1106 1108 } 1107 1109 1108 1110 type RepoPullPatchParams struct { 1109 - LoggedInUser *oauth.User 1111 + LoggedInUser *oauth.MultiAccountUser 1110 1112 RepoInfo repoinfo.RepoInfo 1111 1113 Pull *models.Pull 1112 1114 Stack models.Stack ··· 1123 1125 } 1124 1126 1125 1127 type RepoPullInterdiffParams struct { 1126 - LoggedInUser *oauth.User 1128 + LoggedInUser *oauth.MultiAccountUser 1127 1129 RepoInfo repoinfo.RepoInfo 1128 1130 Pull *models.Pull 1129 1131 Round int ··· 1176 1178 } 1177 1179 1178 1180 type PullResubmitParams struct { 1179 - LoggedInUser *oauth.User 1181 + LoggedInUser *oauth.MultiAccountUser 1180 1182 RepoInfo repoinfo.RepoInfo 1181 1183 Pull *models.Pull 1182 1184 SubmissionId int ··· 1187 1189 } 1188 1190 1189 1191 type PullActionsParams struct { 1190 - LoggedInUser *oauth.User 1192 + LoggedInUser *oauth.MultiAccountUser 1191 1193 RepoInfo repoinfo.RepoInfo 1192 1194 Pull *models.Pull 1193 1195 RoundNumber int ··· 1202 1204 } 1203 1205 1204 1206 type PullNewCommentParams struct { 1205 - LoggedInUser *oauth.User 1207 + LoggedInUser *oauth.MultiAccountUser 1206 1208 RepoInfo repoinfo.RepoInfo 1207 1209 Pull *models.Pull 1208 1210 RoundNumber int ··· 1213 1215 } 1214 1216 1215 1217 type RepoCompareParams struct { 1216 - LoggedInUser *oauth.User 1218 + LoggedInUser *oauth.MultiAccountUser 1217 1219 RepoInfo repoinfo.RepoInfo 1218 1220 Forks []models.Repo 1219 1221 Branches []types.Branch ··· 1232 1234 } 1233 1235 1234 1236 type RepoCompareNewParams struct { 1235 - LoggedInUser *oauth.User 1237 + LoggedInUser *oauth.MultiAccountUser 1236 1238 RepoInfo repoinfo.RepoInfo 1237 1239 Forks []models.Repo 1238 1240 Branches []types.Branch ··· 1249 1251 } 1250 1252 1251 1253 type RepoCompareAllowPullParams struct { 1252 - LoggedInUser *oauth.User 1254 + LoggedInUser *oauth.MultiAccountUser 1253 1255 RepoInfo repoinfo.RepoInfo 1254 1256 Base string 1255 1257 Head string ··· 1269 1271 } 1270 1272 1271 1273 type LabelPanelParams struct { 1272 - LoggedInUser *oauth.User 1274 + LoggedInUser *oauth.MultiAccountUser 1273 1275 RepoInfo repoinfo.RepoInfo 1274 1276 Defs map[string]*models.LabelDefinition 1275 1277 Subject string ··· 1281 1283 } 1282 1284 1283 1285 type EditLabelPanelParams struct { 1284 - LoggedInUser *oauth.User 1286 + LoggedInUser *oauth.MultiAccountUser 1285 1287 RepoInfo repoinfo.RepoInfo 1286 1288 Defs map[string]*models.LabelDefinition 1287 1289 Subject string ··· 1293 1295 } 1294 1296 1295 1297 type PipelinesParams struct { 1296 - LoggedInUser *oauth.User 1298 + LoggedInUser *oauth.MultiAccountUser 1297 1299 RepoInfo repoinfo.RepoInfo 1298 1300 Pipelines []models.Pipeline 1299 1301 Active string ··· 1336 1338 } 1337 1339 1338 1340 type WorkflowParams struct { 1339 - LoggedInUser *oauth.User 1341 + LoggedInUser *oauth.MultiAccountUser 1340 1342 RepoInfo repoinfo.RepoInfo 1341 1343 Pipeline models.Pipeline 1342 1344 Workflow string ··· 1350 1352 } 1351 1353 1352 1354 type PutStringParams struct { 1353 - LoggedInUser *oauth.User 1355 + LoggedInUser *oauth.MultiAccountUser 1354 1356 Action string 1355 1357 1356 1358 // this is supplied in the case of editing an existing string ··· 1362 1364 } 1363 1365 1364 1366 type StringsDashboardParams struct { 1365 - LoggedInUser *oauth.User 1367 + LoggedInUser *oauth.MultiAccountUser 1366 1368 Card ProfileCard 1367 1369 Strings []models.String 1368 1370 } ··· 1372 1374 } 1373 1375 1374 1376 type StringTimelineParams struct { 1375 - LoggedInUser *oauth.User 1377 + LoggedInUser *oauth.MultiAccountUser 1376 1378 Strings []models.String 1377 1379 } 1378 1380 ··· 1381 1383 } 1382 1384 1383 1385 type SingleStringParams struct { 1384 - LoggedInUser *oauth.User 1386 + LoggedInUser *oauth.MultiAccountUser 1385 1387 ShowRendered bool 1386 1388 RenderToggle bool 1387 1389 RenderedContents template.HTML
+49 -11
appview/pages/templates/layouts/fragments/topbar.html
··· 49 49 {{ define "profileDropdown" }} 50 50 <details class="relative inline-block text-left nav-dropdown"> 51 51 <summary class="cursor-pointer list-none flex items-center gap-1"> 52 - {{ $user := .Did }} 52 + {{ $user := .Active.Did }} 53 53 <img 54 54 src="{{ tinyAvatar $user }}" 55 55 alt="" ··· 57 57 /> 58 58 <span class="hidden md:inline">{{ $user | resolve | truncateAt30 }}</span> 59 59 </summary> 60 - <div class="absolute flex flex-col right-0 mt-4 p-4 rounded w-48 bg-white dark:bg-gray-800 dark:text-white border border-gray-200 dark:border-gray-700"> 61 - <a href="/{{ $user }}">profile</a> 62 - <a href="/{{ $user }}?tab=repos">repositories</a> 63 - <a href="/{{ $user }}?tab=strings">strings</a> 64 - <a href="/settings">settings</a> 65 - <a href="#" 66 - hx-post="/logout" 67 - hx-swap="none" 68 - class="text-red-400 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 69 - logout 60 + <div class="absolute right-0 mt-4 p-4 rounded bg-white dark:bg-gray-800 dark:text-white border border-gray-200 dark:border-gray-700 shadow-lg z-50" style="width: 14rem;"> 61 + {{ $active := .Active.Did }} 62 + 63 + <div class="pb-2 mb-2 border-b border-gray-200 dark:border-gray-700"> 64 + <div class="flex items-center gap-2"> 65 + <img src="{{ tinyAvatar $active }}" alt="" class="rounded-full h-8 w-8 flex-shrink-0 border border-gray-300 dark:border-gray-700" /> 66 + <div class="flex-1 overflow-hidden"> 67 + <p class="font-medium text-sm truncate">{{ $active | resolve }}</p> 68 + <p class="text-xs text-green-600 dark:text-green-400">active</p> 69 + </div> 70 + </div> 71 + </div> 72 + 73 + {{ $others := .Accounts | otherAccounts $active }} 74 + {{ if $others }} 75 + <div class="pb-2 mb-2 border-b border-gray-200 dark:border-gray-700"> 76 + <p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">Switch Account</p> 77 + {{ range $others }} 78 + <button 79 + type="button" 80 + hx-post="/account/switch" 81 + hx-vals='{"did": "{{ .Did }}"}' 82 + hx-swap="none" 83 + class="flex items-center gap-2 w-full py-1.5 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left" 84 + > 85 + <img src="{{ tinyAvatar .Did }}" alt="" class="rounded-full h-6 w-6 flex-shrink-0 border border-gray-300 dark:border-gray-700" /> 86 + <span class="text-sm truncate flex-1">{{ .Did | resolve }}</span> 87 + </button> 88 + {{ end }} 89 + </div> 90 + {{ end }} 91 + 92 + <a href="/login?mode=add_account" class="flex items-center gap-2 py-1 text-sm"> 93 + {{ i "plus" "w-4 h-4 flex-shrink-0" }} 94 + <span>Add another account</span> 70 95 </a> 96 + 97 + <div class="pt-2 mt-2 border-t border-gray-200 dark:border-gray-700 space-y-1"> 98 + <a href="/{{ $active }}" class="block py-1 text-sm">profile</a> 99 + <a href="/{{ $active }}?tab=repos" class="block py-1 text-sm">repositories</a> 100 + <a href="/{{ $active }}?tab=strings" class="block py-1 text-sm">strings</a> 101 + <a href="/settings" class="block py-1 text-sm">settings</a> 102 + <a href="#" 103 + hx-post="/logout" 104 + hx-swap="none" 105 + class="block py-1 text-sm text-red-400 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 106 + logout 107 + </a> 108 + </div> 71 109 </div> 72 110 </details> 73 111
+53
appview/pages/templates/user/login.html
··· 20 20 <h2 class="text-center text-xl italic dark:text-white"> 21 21 tightly-knit social coding. 22 22 </h2> 23 + 24 + {{ if .AddAccount }} 25 + <div class="flex gap-2 my-4 bg-blue-50 dark:bg-blue-900/30 border border-blue-300 dark:border-sky-800 rounded px-3 py-2 text-blue-600 dark:text-blue-300"> 26 + <span class="py-1">{{ i "user-plus" "w-4 h-4" }}</span> 27 + <div> 28 + <h5 class="font-medium">Add another account</h5> 29 + <p class="text-sm">Sign in with a different account to add it to your account list.</p> 30 + </div> 31 + </div> 32 + {{ end }} 33 + 34 + {{ if and .LoggedInUser .LoggedInUser.Accounts }} 35 + {{ $accounts := .LoggedInUser.Accounts }} 36 + {{ if $accounts }} 37 + <div class="my-4 border border-gray-200 dark:border-gray-700 rounded overflow-hidden"> 38 + <div class="px-3 py-2 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700"> 39 + <span class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide font-medium">Saved accounts</span> 40 + </div> 41 + <div class="divide-y divide-gray-200 dark:divide-gray-700"> 42 + {{ range $accounts }} 43 + <div class="flex items-center justify-between px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700"> 44 + <button 45 + type="button" 46 + hx-post="/account/switch" 47 + hx-vals='{"did": "{{ .Did }}"}' 48 + hx-swap="none" 49 + class="flex items-center gap-2 flex-1 text-left min-w-0" 50 + > 51 + <img src="{{ tinyAvatar .Did }}" alt="" class="rounded-full h-8 w-8 flex-shrink-0 border border-gray-300 dark:border-gray-700" /> 52 + <div class="flex flex-col min-w-0"> 53 + <span class="text-sm font-medium dark:text-white truncate">{{ .Did | resolve | truncateAt30 }}</span> 54 + <span class="text-xs text-gray-500 dark:text-gray-400">Click to switch</span> 55 + </div> 56 + </button> 57 + <button 58 + type="button" 59 + hx-delete="/account/{{ .Did }}" 60 + hx-swap="none" 61 + class="p-1 text-gray-400 hover:text-red-500 dark:hover:text-red-400 flex-shrink-0" 62 + title="Remove account" 63 + > 64 + {{ i "x" "w-4 h-4" }} 65 + </button> 66 + </div> 67 + {{ end }} 68 + </div> 69 + </div> 70 + {{ end }} 71 + {{ end }} 72 + 23 73 <form 24 74 class="mt-4" 25 75 hx-post="/login" ··· 46 96 </span> 47 97 </div> 48 98 <input type="hidden" name="return_url" value="{{ .ReturnUrl }}"> 99 + <input type="hidden" name="add_account" value="{{ if .AddAccount }}true{{ end }}"> 49 100 50 101 <button 51 102 class="btn w-full my-2 mt-6 text-base " ··· 66 117 You have not authorized the app. 67 118 {{ else if eq .ErrorCode "session" }} 68 119 Server failed to create user session. 120 + {{ else if eq .ErrorCode "max_accounts" }} 121 + You have reached the maximum of 20 linked accounts. Please remove an account before adding a new one. 69 122 {{ else }} 70 123 Internal Server error. 71 124 {{ end }}