mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 02:52:26 -05:00 
			
		
		
		
	[frontend] Profile pages upgrade (#640)
* fix css indentation * profile styling update * update status styling to match profile * empty header fix * generate random avatars for thread views * appease the linter gods * upgrade deps * turn profile accent into border + $bg background * upgrade deps * small accessibility tweaks Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
This commit is contained in:
		
					parent
					
						
							
								8c7945fb78
							
						
					
				
			
			
				commit
				
					
						7c6c0cd547
					
				
			
		
					 9 changed files with 1355 additions and 1301 deletions
				
			
		|  | @ -23,6 +23,7 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"math/rand" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
|  | @ -35,6 +36,21 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | var randAvatars = make(map[string]string) | ||||||
|  | 
 | ||||||
|  | func (m *Module) ensureAvatar(status apimodel.Status) { | ||||||
|  | 	if status.Account.Avatar == "" && len(m.defaultAvatars) > 0 { | ||||||
|  | 		avatar, ok := randAvatars[status.Account.ID] | ||||||
|  | 		if !ok { | ||||||
|  | 			//nolint:gosec | ||||||
|  | 			randomIndex := rand.Intn(len(m.defaultAvatars)) | ||||||
|  | 			avatar = m.defaultAvatars[randomIndex] | ||||||
|  | 			randAvatars[status.Account.ID] = avatar | ||||||
|  | 		} | ||||||
|  | 		status.Account.Avatar = avatar | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (m *Module) threadGETHandler(c *gin.Context) { | func (m *Module) threadGETHandler(c *gin.Context) { | ||||||
| 	ctx := c.Request.Context() | 	ctx := c.Request.Context() | ||||||
| 
 | 
 | ||||||
|  | @ -104,6 +120,16 @@ func (m *Module) threadGETHandler(c *gin.Context) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	m.ensureAvatar(*status) | ||||||
|  | 
 | ||||||
|  | 	for _, status := range context.Descendants { | ||||||
|  | 		m.ensureAvatar(status) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for _, status := range context.Ancestors { | ||||||
|  | 		m.ensureAvatar(status) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	c.HTML(http.StatusOK, "thread.tmpl", gin.H{ | 	c.HTML(http.StatusOK, "thread.tmpl", gin.H{ | ||||||
| 		"instance": instance, | 		"instance": instance, | ||||||
| 		"status":   status, | 		"status":   status, | ||||||
|  |  | ||||||
|  | @ -16,6 +16,9 @@ | ||||||
| 	along with this program.  If not, see <http://www.gnu.org/licenses/>. | 	along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
|  | $br: 0.4rem; | ||||||
|  | $boxshadow: 0 0.4rem 1rem -0.2rem rgba(0,0,0,0.2); | ||||||
|  | 
 | ||||||
| html, body { | html, body { | ||||||
| 	padding: 0; | 	padding: 0; | ||||||
| 	margin: 0; | 	margin: 0; | ||||||
|  |  | ||||||
|  | @ -18,76 +18,89 @@ | ||||||
| 
 | 
 | ||||||
| main { | main { | ||||||
| 	background: transparent; | 	background: transparent; | ||||||
| } | 	padding-top: 0; | ||||||
| 
 |  | ||||||
| .headerimage { |  | ||||||
|   img { |  | ||||||
|     width: 100%; |  | ||||||
|     height: 15em; |  | ||||||
|     object-fit: cover; |  | ||||||
|     border-radius: 10px; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .profile { | .profile { | ||||||
| 	position: relative; | 	position: relative; | ||||||
| 	background: $bg_darker3; | 	background: $bg_darker3; | ||||||
|   padding: 2rem; | 	display: grid; | ||||||
|   display: flex; | 	grid-template-rows: minmax(6rem, 20%) auto auto; | ||||||
|  | 	grid-template-columns: 1fr; | ||||||
| 	flex-wrap: wrap; | 	flex-wrap: wrap; | ||||||
| 	justify-content: space-around; | 	justify-content: space-around; | ||||||
| 	gap: 0.5rem; | 	gap: 0.5rem; | ||||||
| 	margin-bottom: 0.2rem; | 	margin-bottom: 0.2rem; | ||||||
|  | 	border-radius: $br; | ||||||
|  | 
 | ||||||
|  | 	box-shadow: $boxshadow; | ||||||
|  | 
 | ||||||
|  | 	.headerimage { | ||||||
|  | 		height: 100%; | ||||||
|  | 		aspect-ratio: 3 / 1; | ||||||
|  | 		overflow: hidden; | ||||||
|  | 		box-shadow: $boxshadow; | ||||||
|  | 
 | ||||||
|  | 		img { | ||||||
|  | 			width: 100%; | ||||||
|  | 			height: 100%; | ||||||
|  | 			object-fit: cover; | ||||||
|  | 			border-radius: $br $br 0 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	.basic { | 	.basic { | ||||||
|     display: flex; | 		margin-top: -7rem; | ||||||
|     flex-direction: column; | 		padding: 0 1rem; | ||||||
|     flex: 1 1 25em; | 
 | ||||||
|     gap: 0.5rem; | 		display: grid; | ||||||
|  | 		grid-template-columns: auto 1fr; | ||||||
|  | 		grid-template-rows: 6.5rem auto; | ||||||
|  | 
 | ||||||
|  | 		.avatar { | ||||||
|  | 			box-sizing: border-box; | ||||||
|  | 			height: 8.5rem; | ||||||
|  | 			width: 8.5rem; | ||||||
|  | 			grid-row: 1 / span 2; | ||||||
|  | 			background: $bg; | ||||||
|  | 			border: 0.2rem solid $acc2; | ||||||
|  | 			padding: 0; | ||||||
|  | 			border-radius: $br; | ||||||
|  | 			position: relative; | ||||||
|  | 
 | ||||||
|  | 			box-shadow: $boxshadow; | ||||||
|  | 			img { | ||||||
|  | 				object-fit: cover; | ||||||
|  | 				border-radius: $br; | ||||||
|  | 				width: 100%; | ||||||
|  | 				height: 100%; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		a { | 		a { | ||||||
| 			position: relative; | 			position: relative; | ||||||
| 			z-index: 1; | 			z-index: 1; | ||||||
| 			color: inherit; | 			color: inherit; | ||||||
| 			text-decoration: none; | 			text-decoration: none; | ||||||
|  | 			padding: 0.5rem; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|     .avatar-container { |  | ||||||
|       position: relative; |  | ||||||
|       width: 100%; |  | ||||||
|       max-width: 25em; |  | ||||||
| 
 |  | ||||||
|       .avatar { |  | ||||||
|         position: absolute; |  | ||||||
|         top: 0; |  | ||||||
|         left: 0; |  | ||||||
|         bottom: 0; |  | ||||||
|         right: 0; |  | ||||||
| 
 |  | ||||||
|         img { |  | ||||||
|           object-fit: cover; |  | ||||||
|           border-radius: 10px; |  | ||||||
|           width: 100%; |  | ||||||
|           height: 100%; |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .avatar-container:before { |  | ||||||
|       content: ""; |  | ||||||
|       float: left; |  | ||||||
|       padding-top: 100%; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 		.displayname { | 		.displayname { | ||||||
|  | 			align-self: end; | ||||||
|  | 			font-weight: bold; | ||||||
|  | 			font-size: 2rem; | ||||||
|  | 			line-height: 2.2rem; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		.username { | ||||||
|  | 			padding-top: 0.25rem; | ||||||
|  | 			color: $acc1; | ||||||
| 			font-weight: bold; | 			font-weight: bold; | ||||||
|       font-size: 1.6rem; |  | ||||||
|       align-self: start; |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	.detailed { | 	.detailed { | ||||||
|  | 		padding: 0 1rem; | ||||||
| 		display: flex; | 		display: flex; | ||||||
| 		flex-direction: column; | 		flex-direction: column; | ||||||
| 		flex: 1 1 25em; | 		flex: 1 1 25em; | ||||||
|  | @ -108,24 +121,23 @@ main { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .accountstats { | .accountstats { | ||||||
|   position: relative; | 	background: $bg_lighter3; | ||||||
|   background: $bg_darker3; |  | ||||||
|   padding: 0.5rem; |  | ||||||
| 	display: flex; | 	display: flex; | ||||||
| 	flex-wrap: wrap; | 	flex-wrap: wrap; | ||||||
|   justify-content: space-evenly; | 	justify-content: space-between; | ||||||
|   gap: 0.5rem; | 	padding: 0 1.2rem; | ||||||
|   margin-bottom: 0.2rem; | 	border-radius: 0 0 $br $br; | ||||||
| 
 | 
 | ||||||
| 	.entry { | 	.entry { | ||||||
|     background: $bg_lighter3; | 		padding: 1rem 0; | ||||||
|     padding: 0.5rem; |  | ||||||
|     flex-grow: 1; |  | ||||||
| 		text-align: center; | 		text-align: center; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| footer + div { | .toot, .toot:last-child { | ||||||
|   /* something weird from the devstack.. */ | 	box-shadow: $boxshadow; | ||||||
|   display: none; | } | ||||||
|  | 
 | ||||||
|  | #recent { | ||||||
|  | 	margin-left: 1rem; | ||||||
| } | } | ||||||
|  | @ -24,17 +24,20 @@ main { | ||||||
| .thread { | .thread { | ||||||
| 	display: flex; | 	display: flex; | ||||||
| 	flex-direction: column; | 	flex-direction: column; | ||||||
|  | 	border-radius: $br; | ||||||
|  | 	background: $bg_darker5; | ||||||
|  | 	box-shadow: $boxshadow; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .toot { | .toot { | ||||||
| 	position: relative; | 	position: relative; | ||||||
| 	background: $bg_darker3; | 	background: $bg_darker3; | ||||||
| 	padding: 2rem; | 	padding: 1.5rem; | ||||||
| 	/* padding-bottom: 0; */ |  | ||||||
| 	display: grid; | 	display: grid; | ||||||
| 	grid-template-columns: 3.2rem auto 1fr; | 	grid-template-columns: 4rem auto 1fr; | ||||||
| 	column-gap: 0.5rem; | 	column-gap: 0.5rem; | ||||||
| 	margin-bottom: 0.2rem; | 	margin-bottom: $br; | ||||||
|  | 	border-radius: $br; | ||||||
| 
 | 
 | ||||||
| 	a { | 	a { | ||||||
| 		position: relative; | 		position: relative; | ||||||
|  | @ -45,11 +48,16 @@ main { | ||||||
| 
 | 
 | ||||||
| 	.avatar { | 	.avatar { | ||||||
| 		grid-row: span 2; | 		grid-row: span 2; | ||||||
|  | 		aspect-ratio: 1/1; | ||||||
| 
 | 
 | ||||||
| 		img { | 		img { | ||||||
| 			height: 3.2rem; | 			height: 100%; | ||||||
| 			width: 3.2rem; | 			width: 100%; | ||||||
| 			object-fit: cover; | 			object-fit: cover; | ||||||
|  | 			background: $bg; | ||||||
|  | 			border: 0.1rem solid $acc2; | ||||||
|  | 			/* box-sizing: border-box; */ | ||||||
|  | 			border-radius: calc($br / 1.5); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -78,6 +86,9 @@ main { | ||||||
| 			z-index: 2; | 			z-index: 2; | ||||||
| 			cursor: pointer; | 			cursor: pointer; | ||||||
| 		} | 		} | ||||||
|  | 		label:hover { | ||||||
|  | 			background: $acc2; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	.text { | 	.text { | ||||||
|  | @ -203,16 +214,16 @@ main { | ||||||
| 		z-index: 0; | 		z-index: 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	$border-radius: 0.3rem; |  | ||||||
| 	&:first-child { | 	&:first-child { | ||||||
| 		/* top left, top right */ | 		/* top left, top right */ | ||||||
| 		border-radius: $border-radius $border-radius 0 0; | 		border-radius: $br $br 0 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	&:last-child { | 	&:last-child { | ||||||
| 		/* bottom left, bottom right */ | 		/* bottom left, bottom right */ | ||||||
| 		border-radius: 0 0 $border-radius $border-radius; | 		border-radius: 0 0 $br $br; | ||||||
| 		padding-bottom: 1.5rem; | 		padding-bottom: 1.5rem; | ||||||
|  | 		margin-bottom: 0; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	&.expanded { | 	&.expanded { | ||||||
|  |  | ||||||
							
								
								
									
										2258
									
								
								web/source/yarn.lock
									
										
									
									
									
								
							
							
						
						
									
										2258
									
								
								web/source/yarn.lock
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -18,7 +18,7 @@ | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
| 	<div class="page"> | 	<div class="page"> | ||||||
| 		<a href="/" class="nounderline"> | 		<a aria-label="instance homepage" href="/" class="nounderline header"> | ||||||
| 			<header> | 			<header> | ||||||
| 				<img src="/assets/logo.png" alt="Instance Logo"/> | 				<img src="/assets/logo.png" alt="Instance Logo"/> | ||||||
| 				<div> | 				<div> | ||||||
|  |  | ||||||
|  | @ -1,28 +1,32 @@ | ||||||
| {{ template "header.tmpl" .}} | {{ template "header.tmpl" .}} | ||||||
| <main> | <main> | ||||||
|     {{ if .account.Header }}<a href="{{.account.Header}}" class="headerimage"><img src="{{.account.Header}}"></a>{{ end }} |  | ||||||
|     <div class="profile"> |     <div class="profile"> | ||||||
|  |         <div class="headerimage"> | ||||||
|  |             {{ if .account.Header }} | ||||||
|  |             <img | ||||||
|  |                 src="{{.account.Header}}" | ||||||
|  |                 alt="{{if .account.DisplayName}}{{.account.DisplayName}}{{else}}{{.account.Username}}{{end}}'s header" | ||||||
|  |             /> | ||||||
|  |             {{ end }} | ||||||
|  |         </div> | ||||||
|         <div class="basic"> |         <div class="basic"> | ||||||
|  |             <a href="{{.account.Avatar}}" class="avatar"><img src="{{.account.Avatar}}" alt="{{if .account.DisplayName}}{{.account.DisplayName}}{{else}}{{.account.Username}}{{end}}'s avatar"></a> | ||||||
|             <a href="{{.account.URL}}" class="displayname">{{if .account.DisplayName}}{{.account.DisplayName}}{{else}}{{.account.Username}}{{end}}</a> |             <a href="{{.account.URL}}" class="displayname">{{if .account.DisplayName}}{{.account.DisplayName}}{{else}}{{.account.Username}}{{end}}</a> | ||||||
|             <a href="{{.account.URL}}" class="username">@{{.account.Username}}</a> |             <a href="{{.account.URL}}" class="username">@{{.account.Username}}</a> | ||||||
|             <div class="avatar-container"> |  | ||||||
|                 <a href="{{.account.Avatar}}" class="avatar"><img src="{{.account.Avatar}}"></a> |  | ||||||
|             </div> |  | ||||||
|         </div> |         </div> | ||||||
|         <div class="detailed"> |         <div class="detailed"> | ||||||
|             <h2>About @{{.account.Username}}</h2> |  | ||||||
|             <div class="bio"> |             <div class="bio"> | ||||||
|                 {{ if .account.Note }}{{ .account.Note | noescape }}{{else}}This GoToSocial user hasn't written a bio yet!{{end}} |                 {{ if .account.Note }}{{ .account.Note | noescape }}{{else}}This GoToSocial user hasn't written a bio yet!{{end}} | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|     </div> |  | ||||||
|         <div class="accountstats"> |         <div class="accountstats"> | ||||||
|         <div class="entry">Joined {{.account.CreatedAt | timestampShort}}</div> |             <div class="entry">Joined <b>{{.account.CreatedAt | timestampShort}}</b></div> | ||||||
|         <div class="entry">Followed by {{.account.FollowersCount}}</div> |             <div class="entry">Followed by <b>{{.account.FollowersCount}}</b></div> | ||||||
|         <div class="entry">Following {{.account.FollowingCount}}</div> |             <div class="entry">Following <b>{{.account.FollowingCount}}</b></div> | ||||||
|         <div class="entry">Posted {{.account.StatusesCount}}</div> |             <div class="entry">Posted <b>{{.account.StatusesCount}}</b></div> | ||||||
|         </div> |         </div> | ||||||
|     <h2>Recent public posts by @{{.account.Username}}</h2> |     </div> | ||||||
|  |     <h2 id="recent">Recent public toots</h2> | ||||||
| 	<div class="thread"> | 	<div class="thread"> | ||||||
| 		{{range .statuses}} | 		{{range .statuses}} | ||||||
| 		<div class="toot expanded"> | 		<div class="toot expanded"> | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| <a href="{{.Account.URL}}" class="avatar"><img src="{{.Account.Avatar}}"></a> | <a href="{{.Account.URL}}" class="avatar"><img src="{{.Account.Avatar}}" alt=""></a> | ||||||
| <a href="{{.Account.URL}}" class="displayname">{{if .Account.DisplayName}}{{.Account.DisplayName}}{{else}}{{.Account.Username}}{{end}}</a> | <a href="{{.Account.URL}}" class="displayname">{{if .Account.DisplayName}}{{.Account.DisplayName}}{{else}}{{.Account.Username}}{{end}}</a> | ||||||
| <a href="{{.Account.URL}}" class="username">@{{.Account.Username}}</a> | <a href="{{.Account.URL}}" class="username">@{{.Account.Username}}</a> | ||||||
| <div class="text"> | <div class="text"> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue