Monorepo for Tangled

pages: support MultiAccountUser in templates and params

+180 -77
+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
··· 226 226 } 227 227 228 228 type LoginParams struct { 229 - ReturnUrl string 230 - ErrorCode string 229 + ReturnUrl string 230 + ErrorCode string 231 + AddAccount bool 232 + LoggedInUser *oauth.MultiAccountUser 231 233 } 232 234 233 235 func (p *Pages) Login(w io.Writer, params LoginParams) error { ··· 247 249 } 248 250 249 251 type TermsOfServiceParams struct { 250 - LoggedInUser *oauth.User 252 + LoggedInUser *oauth.MultiAccountUser 251 253 Content template.HTML 252 254 } 253 255 ··· 275 277 } 276 278 277 279 type PrivacyPolicyParams struct { 278 - LoggedInUser *oauth.User 280 + LoggedInUser *oauth.MultiAccountUser 279 281 Content template.HTML 280 282 } 281 283 ··· 303 305 } 304 306 305 307 type BrandParams struct { 306 - LoggedInUser *oauth.User 308 + LoggedInUser *oauth.MultiAccountUser 307 309 } 308 310 309 311 func (p *Pages) Brand(w io.Writer, params BrandParams) error { ··· 311 313 } 312 314 313 315 type TimelineParams struct { 314 - LoggedInUser *oauth.User 316 + LoggedInUser *oauth.MultiAccountUser 315 317 Timeline []models.TimelineEvent 316 318 Repos []models.Repo 317 319 GfiLabel *models.LabelDefinition ··· 322 324 } 323 325 324 326 type GoodFirstIssuesParams struct { 325 - LoggedInUser *oauth.User 327 + LoggedInUser *oauth.MultiAccountUser 326 328 Issues []models.Issue 327 329 RepoGroups []*models.RepoGroup 328 330 LabelDefs map[string]*models.LabelDefinition ··· 335 337 } 336 338 337 339 type UserProfileSettingsParams struct { 338 - LoggedInUser *oauth.User 340 + LoggedInUser *oauth.MultiAccountUser 339 341 Tabs []map[string]any 340 342 Tab string 341 343 } ··· 345 347 } 346 348 347 349 type NotificationsParams struct { 348 - LoggedInUser *oauth.User 350 + LoggedInUser *oauth.MultiAccountUser 349 351 Notifications []*models.NotificationWithEntity 350 352 UnreadCount int 351 353 Page pagination.Page ··· 373 375 } 374 376 375 377 type UserKeysSettingsParams struct { 376 - LoggedInUser *oauth.User 378 + LoggedInUser *oauth.MultiAccountUser 377 379 PubKeys []models.PublicKey 378 380 Tabs []map[string]any 379 381 Tab string ··· 384 386 } 385 387 386 388 type UserEmailsSettingsParams struct { 387 - LoggedInUser *oauth.User 389 + LoggedInUser *oauth.MultiAccountUser 388 390 Emails []models.Email 389 391 Tabs []map[string]any 390 392 Tab string ··· 395 397 } 396 398 397 399 type UserNotificationSettingsParams struct { 398 - LoggedInUser *oauth.User 400 + LoggedInUser *oauth.MultiAccountUser 399 401 Preferences *models.NotificationPreferences 400 402 Tabs []map[string]any 401 403 Tab string ··· 415 417 } 416 418 417 419 type KnotsParams struct { 418 - LoggedInUser *oauth.User 420 + LoggedInUser *oauth.MultiAccountUser 419 421 Registrations []models.Registration 420 422 Tabs []map[string]any 421 423 Tab string ··· 426 428 } 427 429 428 430 type KnotParams struct { 429 - LoggedInUser *oauth.User 431 + LoggedInUser *oauth.MultiAccountUser 430 432 Registration *models.Registration 431 433 Members []string 432 434 Repos map[string][]models.Repo ··· 448 450 } 449 451 450 452 type SpindlesParams struct { 451 - LoggedInUser *oauth.User 453 + LoggedInUser *oauth.MultiAccountUser 452 454 Spindles []models.Spindle 453 455 Tabs []map[string]any 454 456 Tab string ··· 469 471 } 470 472 471 473 type SpindleDashboardParams struct { 472 - LoggedInUser *oauth.User 474 + LoggedInUser *oauth.MultiAccountUser 473 475 Spindle models.Spindle 474 476 Members []string 475 477 Repos map[string][]models.Repo ··· 482 484 } 483 485 484 486 type NewRepoParams struct { 485 - LoggedInUser *oauth.User 487 + LoggedInUser *oauth.MultiAccountUser 486 488 Knots []string 487 489 } 488 490 ··· 491 493 } 492 494 493 495 type ForkRepoParams struct { 494 - LoggedInUser *oauth.User 496 + LoggedInUser *oauth.MultiAccountUser 495 497 Knots []string 496 498 RepoInfo repoinfo.RepoInfo 497 499 } ··· 529 531 } 530 532 531 533 type ProfileOverviewParams struct { 532 - LoggedInUser *oauth.User 534 + LoggedInUser *oauth.MultiAccountUser 533 535 Repos []models.Repo 534 536 CollaboratingRepos []models.Repo 535 537 ProfileTimeline *models.ProfileTimeline ··· 543 545 } 544 546 545 547 type ProfileReposParams struct { 546 - LoggedInUser *oauth.User 548 + LoggedInUser *oauth.MultiAccountUser 547 549 Repos []models.Repo 548 550 Card *ProfileCard 549 551 Active string ··· 555 557 } 556 558 557 559 type ProfileStarredParams struct { 558 - LoggedInUser *oauth.User 560 + LoggedInUser *oauth.MultiAccountUser 559 561 Repos []models.Repo 560 562 Card *ProfileCard 561 563 Active string ··· 567 569 } 568 570 569 571 type ProfileStringsParams struct { 570 - LoggedInUser *oauth.User 572 + LoggedInUser *oauth.MultiAccountUser 571 573 Strings []models.String 572 574 Card *ProfileCard 573 575 Active string ··· 580 582 581 583 type FollowCard struct { 582 584 UserDid string 583 - LoggedInUser *oauth.User 585 + LoggedInUser *oauth.MultiAccountUser 584 586 FollowStatus models.FollowStatus 585 587 FollowersCount int64 586 588 FollowingCount int64 ··· 588 590 } 589 591 590 592 type ProfileFollowersParams struct { 591 - LoggedInUser *oauth.User 593 + LoggedInUser *oauth.MultiAccountUser 592 594 Followers []FollowCard 593 595 Card *ProfileCard 594 596 Active string ··· 600 602 } 601 603 602 604 type ProfileFollowingParams struct { 603 - LoggedInUser *oauth.User 605 + LoggedInUser *oauth.MultiAccountUser 604 606 Following []FollowCard 605 607 Card *ProfileCard 606 608 Active string ··· 622 624 } 623 625 624 626 type EditBioParams struct { 625 - LoggedInUser *oauth.User 627 + LoggedInUser *oauth.MultiAccountUser 626 628 Profile *models.Profile 627 629 } 628 630 ··· 631 633 } 632 634 633 635 type EditPinsParams struct { 634 - LoggedInUser *oauth.User 636 + LoggedInUser *oauth.MultiAccountUser 635 637 Profile *models.Profile 636 638 AllRepos []PinnedRepo 637 639 } ··· 658 660 } 659 661 660 662 type RepoIndexParams struct { 661 - LoggedInUser *oauth.User 663 + LoggedInUser *oauth.MultiAccountUser 662 664 RepoInfo repoinfo.RepoInfo 663 665 Active string 664 666 TagMap map[string][]string ··· 707 709 } 708 710 709 711 type RepoLogParams struct { 710 - LoggedInUser *oauth.User 712 + LoggedInUser *oauth.MultiAccountUser 711 713 RepoInfo repoinfo.RepoInfo 712 714 TagMap map[string][]string 713 715 Active string ··· 724 726 } 725 727 726 728 type RepoCommitParams struct { 727 - LoggedInUser *oauth.User 729 + LoggedInUser *oauth.MultiAccountUser 728 730 RepoInfo repoinfo.RepoInfo 729 731 Active string 730 732 EmailToDid map[string]string ··· 743 745 } 744 746 745 747 type RepoTreeParams struct { 746 - LoggedInUser *oauth.User 748 + LoggedInUser *oauth.MultiAccountUser 747 749 RepoInfo repoinfo.RepoInfo 748 750 Active string 749 751 BreadCrumbs [][]string ··· 798 800 } 799 801 800 802 type RepoBranchesParams struct { 801 - LoggedInUser *oauth.User 803 + LoggedInUser *oauth.MultiAccountUser 802 804 RepoInfo repoinfo.RepoInfo 803 805 Active string 804 806 types.RepoBranchesResponse ··· 810 812 } 811 813 812 814 type RepoTagsParams struct { 813 - LoggedInUser *oauth.User 815 + LoggedInUser *oauth.MultiAccountUser 814 816 RepoInfo repoinfo.RepoInfo 815 817 Active string 816 818 types.RepoTagsResponse ··· 824 826 } 825 827 826 828 type RepoArtifactParams struct { 827 - LoggedInUser *oauth.User 829 + LoggedInUser *oauth.MultiAccountUser 828 830 RepoInfo repoinfo.RepoInfo 829 831 Artifact models.Artifact 830 832 } ··· 834 836 } 835 837 836 838 type RepoBlobParams struct { 837 - LoggedInUser *oauth.User 839 + LoggedInUser *oauth.MultiAccountUser 838 840 RepoInfo repoinfo.RepoInfo 839 841 Active string 840 842 BreadCrumbs [][]string ··· 858 860 } 859 861 860 862 type RepoSettingsParams struct { 861 - LoggedInUser *oauth.User 863 + LoggedInUser *oauth.MultiAccountUser 862 864 RepoInfo repoinfo.RepoInfo 863 865 Collaborators []Collaborator 864 866 Active string ··· 877 879 } 878 880 879 881 type RepoGeneralSettingsParams struct { 880 - LoggedInUser *oauth.User 882 + LoggedInUser *oauth.MultiAccountUser 881 883 RepoInfo repoinfo.RepoInfo 882 884 Labels []models.LabelDefinition 883 885 DefaultLabels []models.LabelDefinition ··· 895 897 } 896 898 897 899 type RepoAccessSettingsParams struct { 898 - LoggedInUser *oauth.User 900 + LoggedInUser *oauth.MultiAccountUser 899 901 RepoInfo repoinfo.RepoInfo 900 902 Active string 901 903 Tabs []map[string]any ··· 909 911 } 910 912 911 913 type RepoPipelineSettingsParams struct { 912 - LoggedInUser *oauth.User 914 + LoggedInUser *oauth.MultiAccountUser 913 915 RepoInfo repoinfo.RepoInfo 914 916 Active string 915 917 Tabs []map[string]any ··· 925 927 } 926 928 927 929 type RepoIssuesParams struct { 928 - LoggedInUser *oauth.User 930 + LoggedInUser *oauth.MultiAccountUser 929 931 RepoInfo repoinfo.RepoInfo 930 932 Active string 931 933 Issues []models.Issue ··· 942 944 } 943 945 944 946 type RepoSingleIssueParams struct { 945 - LoggedInUser *oauth.User 947 + LoggedInUser *oauth.MultiAccountUser 946 948 RepoInfo repoinfo.RepoInfo 947 949 Active string 948 950 Issue *models.Issue ··· 961 963 } 962 964 963 965 type EditIssueParams struct { 964 - LoggedInUser *oauth.User 966 + LoggedInUser *oauth.MultiAccountUser 965 967 RepoInfo repoinfo.RepoInfo 966 968 Issue *models.Issue 967 969 Action string ··· 985 987 } 986 988 987 989 type RepoNewIssueParams struct { 988 - LoggedInUser *oauth.User 990 + LoggedInUser *oauth.MultiAccountUser 989 991 RepoInfo repoinfo.RepoInfo 990 992 Issue *models.Issue // existing issue if any -- passed when editing 991 993 Active string ··· 999 1001 } 1000 1002 1001 1003 type EditIssueCommentParams struct { 1002 - LoggedInUser *oauth.User 1004 + LoggedInUser *oauth.MultiAccountUser 1003 1005 RepoInfo repoinfo.RepoInfo 1004 1006 Issue *models.Issue 1005 1007 Comment *models.IssueComment ··· 1010 1012 } 1011 1013 1012 1014 type ReplyIssueCommentPlaceholderParams struct { 1013 - LoggedInUser *oauth.User 1015 + LoggedInUser *oauth.MultiAccountUser 1014 1016 RepoInfo repoinfo.RepoInfo 1015 1017 Issue *models.Issue 1016 1018 Comment *models.IssueComment ··· 1021 1023 } 1022 1024 1023 1025 type ReplyIssueCommentParams struct { 1024 - LoggedInUser *oauth.User 1026 + LoggedInUser *oauth.MultiAccountUser 1025 1027 RepoInfo repoinfo.RepoInfo 1026 1028 Issue *models.Issue 1027 1029 Comment *models.IssueComment ··· 1032 1034 } 1033 1035 1034 1036 type IssueCommentBodyParams struct { 1035 - LoggedInUser *oauth.User 1037 + LoggedInUser *oauth.MultiAccountUser 1036 1038 RepoInfo repoinfo.RepoInfo 1037 1039 Issue *models.Issue 1038 1040 Comment *models.IssueComment ··· 1043 1045 } 1044 1046 1045 1047 type RepoNewPullParams struct { 1046 - LoggedInUser *oauth.User 1048 + LoggedInUser *oauth.MultiAccountUser 1047 1049 RepoInfo repoinfo.RepoInfo 1048 1050 Branches []types.Branch 1049 1051 Strategy string ··· 1060 1062 } 1061 1063 1062 1064 type RepoPullsParams struct { 1063 - LoggedInUser *oauth.User 1065 + LoggedInUser *oauth.MultiAccountUser 1064 1066 RepoInfo repoinfo.RepoInfo 1065 1067 Pulls []*models.Pull 1066 1068 Active string ··· 1097 1099 } 1098 1100 1099 1101 type RepoSinglePullParams struct { 1100 - LoggedInUser *oauth.User 1102 + LoggedInUser *oauth.MultiAccountUser 1101 1103 RepoInfo repoinfo.RepoInfo 1102 1104 Active string 1103 1105 Pull *models.Pull ··· 1122 1124 } 1123 1125 1124 1126 type RepoPullPatchParams struct { 1125 - LoggedInUser *oauth.User 1127 + LoggedInUser *oauth.MultiAccountUser 1126 1128 RepoInfo repoinfo.RepoInfo 1127 1129 Pull *models.Pull 1128 1130 Stack models.Stack ··· 1139 1141 } 1140 1142 1141 1143 type RepoPullInterdiffParams struct { 1142 - LoggedInUser *oauth.User 1144 + LoggedInUser *oauth.MultiAccountUser 1143 1145 RepoInfo repoinfo.RepoInfo 1144 1146 Pull *models.Pull 1145 1147 Round int ··· 1192 1194 } 1193 1195 1194 1196 type PullResubmitParams struct { 1195 - LoggedInUser *oauth.User 1197 + LoggedInUser *oauth.MultiAccountUser 1196 1198 RepoInfo repoinfo.RepoInfo 1197 1199 Pull *models.Pull 1198 1200 SubmissionId int ··· 1203 1205 } 1204 1206 1205 1207 type PullActionsParams struct { 1206 - LoggedInUser *oauth.User 1208 + LoggedInUser *oauth.MultiAccountUser 1207 1209 RepoInfo repoinfo.RepoInfo 1208 1210 Pull *models.Pull 1209 1211 RoundNumber int ··· 1218 1220 } 1219 1221 1220 1222 type PullNewCommentParams struct { 1221 - LoggedInUser *oauth.User 1223 + LoggedInUser *oauth.MultiAccountUser 1222 1224 RepoInfo repoinfo.RepoInfo 1223 1225 Pull *models.Pull 1224 1226 RoundNumber int ··· 1229 1231 } 1230 1232 1231 1233 type RepoCompareParams struct { 1232 - LoggedInUser *oauth.User 1234 + LoggedInUser *oauth.MultiAccountUser 1233 1235 RepoInfo repoinfo.RepoInfo 1234 1236 Forks []models.Repo 1235 1237 Branches []types.Branch ··· 1248 1250 } 1249 1251 1250 1252 type RepoCompareNewParams struct { 1251 - LoggedInUser *oauth.User 1253 + LoggedInUser *oauth.MultiAccountUser 1252 1254 RepoInfo repoinfo.RepoInfo 1253 1255 Forks []models.Repo 1254 1256 Branches []types.Branch ··· 1265 1267 } 1266 1268 1267 1269 type RepoCompareAllowPullParams struct { 1268 - LoggedInUser *oauth.User 1270 + LoggedInUser *oauth.MultiAccountUser 1269 1271 RepoInfo repoinfo.RepoInfo 1270 1272 Base string 1271 1273 Head string ··· 1285 1287 } 1286 1288 1287 1289 type LabelPanelParams struct { 1288 - LoggedInUser *oauth.User 1290 + LoggedInUser *oauth.MultiAccountUser 1289 1291 RepoInfo repoinfo.RepoInfo 1290 1292 Defs map[string]*models.LabelDefinition 1291 1293 Subject string ··· 1297 1299 } 1298 1300 1299 1301 type EditLabelPanelParams struct { 1300 - LoggedInUser *oauth.User 1302 + LoggedInUser *oauth.MultiAccountUser 1301 1303 RepoInfo repoinfo.RepoInfo 1302 1304 Defs map[string]*models.LabelDefinition 1303 1305 Subject string ··· 1309 1311 } 1310 1312 1311 1313 type PipelinesParams struct { 1312 - LoggedInUser *oauth.User 1314 + LoggedInUser *oauth.MultiAccountUser 1313 1315 RepoInfo repoinfo.RepoInfo 1314 1316 Pipelines []models.Pipeline 1315 1317 Active string ··· 1352 1354 } 1353 1355 1354 1356 type WorkflowParams struct { 1355 - LoggedInUser *oauth.User 1357 + LoggedInUser *oauth.MultiAccountUser 1356 1358 RepoInfo repoinfo.RepoInfo 1357 1359 Pipeline models.Pipeline 1358 1360 Workflow string ··· 1366 1368 } 1367 1369 1368 1370 type PutStringParams struct { 1369 - LoggedInUser *oauth.User 1371 + LoggedInUser *oauth.MultiAccountUser 1370 1372 Action string 1371 1373 1372 1374 // this is supplied in the case of editing an existing string ··· 1378 1380 } 1379 1381 1380 1382 type StringsDashboardParams struct { 1381 - LoggedInUser *oauth.User 1383 + LoggedInUser *oauth.MultiAccountUser 1382 1384 Card ProfileCard 1383 1385 Strings []models.String 1384 1386 } ··· 1388 1390 } 1389 1391 1390 1392 type StringTimelineParams struct { 1391 - LoggedInUser *oauth.User 1393 + LoggedInUser *oauth.MultiAccountUser 1392 1394 Strings []models.String 1393 1395 } 1394 1396 ··· 1397 1399 } 1398 1400 1399 1401 type SingleStringParams struct { 1400 - LoggedInUser *oauth.User 1402 + LoggedInUser *oauth.MultiAccountUser 1401 1403 ShowRendered bool 1402 1404 RenderToggle bool 1403 1405 RenderedContents template.HTML
+49 -11
appview/pages/templates/layouts/fragments/topbar.html
··· 45 45 {{ define "profileDropdown" }} 46 46 <details class="relative inline-block text-left nav-dropdown"> 47 47 <summary class="cursor-pointer list-none flex items-center gap-1"> 48 - {{ $user := .Did }} 48 + {{ $user := .Active.Did }} 49 49 <img 50 50 src="{{ tinyAvatar $user }}" 51 51 alt="" ··· 53 53 /> 54 54 <span class="hidden md:inline">{{ $user | resolve | truncateAt30 }}</span> 55 55 </summary> 56 - <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"> 57 - <a href="/{{ $user }}">profile</a> 58 - <a href="/{{ $user }}?tab=repos">repositories</a> 59 - <a href="/{{ $user }}?tab=strings">strings</a> 60 - <a href="/settings">settings</a> 61 - <a href="#" 62 - hx-post="/logout" 63 - hx-swap="none" 64 - class="text-red-400 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 65 - logout 56 + <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;"> 57 + {{ $active := .Active.Did }} 58 + 59 + <div class="pb-2 mb-2 border-b border-gray-200 dark:border-gray-700"> 60 + <div class="flex items-center gap-2"> 61 + <img src="{{ tinyAvatar $active }}" alt="" class="rounded-full h-8 w-8 flex-shrink-0 border border-gray-300 dark:border-gray-700" /> 62 + <div class="flex-1 overflow-hidden"> 63 + <p class="font-medium text-sm truncate">{{ $active | resolve }}</p> 64 + <p class="text-xs text-green-600 dark:text-green-400">active</p> 65 + </div> 66 + </div> 67 + </div> 68 + 69 + {{ $others := .Accounts | otherAccounts $active }} 70 + {{ if $others }} 71 + <div class="pb-2 mb-2 border-b border-gray-200 dark:border-gray-700"> 72 + <p class="text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">Switch Account</p> 73 + {{ range $others }} 74 + <button 75 + type="button" 76 + hx-post="/account/switch" 77 + hx-vals='{"did": "{{ .Did }}"}' 78 + hx-swap="none" 79 + class="flex items-center gap-2 w-full py-1.5 rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-left" 80 + > 81 + <img src="{{ tinyAvatar .Did }}" alt="" class="rounded-full h-6 w-6 flex-shrink-0 border border-gray-300 dark:border-gray-700" /> 82 + <span class="text-sm truncate flex-1">{{ .Did | resolve }}</span> 83 + </button> 84 + {{ end }} 85 + </div> 86 + {{ end }} 87 + 88 + <a href="/login?mode=add_account" class="flex items-center gap-2 py-1 text-sm"> 89 + {{ i "plus" "w-4 h-4 flex-shrink-0" }} 90 + <span>Add another account</span> 66 91 </a> 92 + 93 + <div class="pt-2 mt-2 border-t border-gray-200 dark:border-gray-700 space-y-1"> 94 + <a href="/{{ $active }}" class="block py-1 text-sm">profile</a> 95 + <a href="/{{ $active }}?tab=repos" class="block py-1 text-sm">repositories</a> 96 + <a href="/{{ $active }}?tab=strings" class="block py-1 text-sm">strings</a> 97 + <a href="/settings" class="block py-1 text-sm">settings</a> 98 + <a href="#" 99 + hx-post="/logout" 100 + hx-swap="none" 101 + class="block py-1 text-sm text-red-400 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 102 + logout 103 + </a> 104 + </div> 67 105 </div> 68 106 </details> 69 107
+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 }}