Remove fetch request from heatmap (#13623)
* Remove fetch request from heatmap Render heatmap data directly to HTML, eliminating one HTTP request on frontpage and user profile. Also added min-height to the container so the page content will no longer move after loading. * rename and error display * also log the js error * add error handler * remove useless inline style and hide divider on small screens * Update routers/user/home.go * Update routers/user/profile.go
This commit is contained in:
		
							parent
							
								
									d02c3508e6
								
							
						
					
					
						commit
						12c2efb45c
					
				
					 11 changed files with 90 additions and 78 deletions
				
			
		|  | @ -114,8 +114,14 @@ func Dashboard(ctx *context.Context) { | |||
| 	ctx.Data["SearchLimit"] = setting.UI.User.RepoPagingNum | ||||
| 	// no heatmap access for admins; GetUserHeatmapDataByUser ignores the calling user
 | ||||
| 	// so everyone would get the same empty heatmap
 | ||||
| 	ctx.Data["EnableHeatmap"] = setting.Service.EnableUserHeatmap && !ctxUser.KeepActivityPrivate | ||||
| 	ctx.Data["HeatmapUser"] = ctxUser.Name | ||||
| 	if setting.Service.EnableUserHeatmap && !ctxUser.KeepActivityPrivate { | ||||
| 		data, err := models.GetUserHeatmapDataByUser(ctxUser) | ||||
| 		if err != nil { | ||||
| 			ctx.ServerError("GetUserHeatmapDataByUser", err) | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.Data["HeatmapData"] = data | ||||
| 	} | ||||
| 
 | ||||
| 	var err error | ||||
| 	var mirrors []*models.Repository | ||||
|  |  | |||
|  | @ -94,10 +94,18 @@ func Profile(ctx *context.Context) { | |||
| 	ctx.Data["PageIsUserProfile"] = true | ||||
| 	ctx.Data["Owner"] = ctxUser | ||||
| 	ctx.Data["OpenIDs"] = openIDs | ||||
| 
 | ||||
| 	// no heatmap access for admins; GetUserHeatmapDataByUser ignores the calling user
 | ||||
| 	// so everyone would get the same empty heatmap
 | ||||
| 	ctx.Data["EnableHeatmap"] = setting.Service.EnableUserHeatmap && !ctxUser.KeepActivityPrivate | ||||
| 	ctx.Data["HeatmapUser"] = ctxUser.Name | ||||
| 	if setting.Service.EnableUserHeatmap && !ctxUser.KeepActivityPrivate { | ||||
| 		data, err := models.GetUserHeatmapDataByUser(ctxUser) | ||||
| 		if err != nil { | ||||
| 			ctx.ServerError("GetUserHeatmapDataByUser", err) | ||||
| 			return | ||||
| 		} | ||||
| 		ctx.Data["HeatmapData"] = data | ||||
| 	} | ||||
| 
 | ||||
| 	if len(ctxUser.Description) != 0 { | ||||
| 		ctx.Data["RenderedDescription"] = string(markdown.Render([]byte(ctxUser.Description), ctx.Repo.RepoLink, map[string]string{"mode": "document"})) | ||||
| 	} | ||||
|  |  | |||
|  | @ -36,8 +36,6 @@ | |||
| 			SimpleMDE: {{if .RequireSimpleMDE}}true{{else}}false{{end}}, | ||||
| 			Tribute: {{if .RequireTribute}}true{{else}}false{{end}}, | ||||
| 			U2F: {{if .RequireU2F}}true{{else}}false{{end}}, | ||||
| 			Heatmap: {{if .EnableHeatmap}}true{{else}}false{{end}}, | ||||
| 			heatmapUser: {{if .HeatmapUser}}'{{.HeatmapUser}}'{{else}}null{{end}}, | ||||
| 			NotificationSettings: { | ||||
| 				MinTimeout: {{NotificationSettings.MinTimeout}}, | ||||
| 				TimeoutStep:  {{NotificationSettings.TimeoutStep}}, | ||||
|  |  | |||
|  | @ -5,10 +5,7 @@ | |||
| 		{{template "base/alert" .}} | ||||
| 		<div class="ui mobile reversed stackable grid"> | ||||
| 			<div class="ui container ten wide column"> | ||||
| 				{{if .EnableHeatmap}} | ||||
| 					{{template "user/dashboard/heatmap" .}} | ||||
| 					<div class="ui divider"></div> | ||||
| 				{{end}} | ||||
| 				{{template "user/heatmap" .}} | ||||
| 				{{template "user/dashboard/feeds" .}} | ||||
| 			</div> | ||||
| 			{{template "user/dashboard/repolist" .}} | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| <div id="user-heatmap" style="padding-right: 40px"> | ||||
| 	<activity-heatmap :locale="locale" :suburl="suburl" :user="heatmapUser"> | ||||
| {{if .HeatmapData}} | ||||
| 	<div id="user-heatmap" data-heatmap-data="{{Json .HeatmapData}}"> | ||||
| 		<div slot="loading"> | ||||
| 			<div class="ui active centered inline indeterminate text loader" id="loading-heatmap">{{.i18n.Tr "user.heatmap.loading"}}</div> | ||||
| 		</div> | ||||
| 	</activity-heatmap> | ||||
| </div> | ||||
| 	</div> | ||||
| 	<div class="ui divider"></div> | ||||
| {{end}} | ||||
|  | @ -108,10 +108,7 @@ | |||
| 							<p>{{.i18n.Tr "user.disabled_public_activity"}}</p> | ||||
| 						</div> | ||||
| 					{{end}} | ||||
| 					{{if .EnableHeatmap}} | ||||
| 						{{template "user/dashboard/heatmap" .}} | ||||
| 						<div class="ui divider"></div> | ||||
| 					{{end}} | ||||
| 					{{template "user/heatmap" .}} | ||||
| 					<div class="feeds"> | ||||
| 						{{template "user/dashboard/feeds" .}} | ||||
| 					</div> | ||||
|  |  | |||
|  | @ -1,13 +1,9 @@ | |||
| <template> | ||||
|   <div class="heatmap-container"> | ||||
|     <div v-show="isLoading"> | ||||
|       <slot name="loading"/> | ||||
|     </div> | ||||
|     <div v-if="!isLoading" class="total-contributions"> | ||||
|   <div id="user-heatmap"> | ||||
|     <div class="total-contributions"> | ||||
|       {{ values.length }} contributions in the last 12 months | ||||
|     </div> | ||||
|     <calendar-heatmap | ||||
|       v-show="!isLoading" | ||||
|       :locale="locale" | ||||
|       :no-data-text="locale.no_contributions" | ||||
|       :tooltip-unit="locale.contributions" | ||||
|  | @ -19,13 +15,17 @@ | |||
| </template> | ||||
| <script> | ||||
| import {CalendarHeatmap} from 'vue-calendar-heatmap'; | ||||
| const {AppSubUrl, heatmapUser} = window.config; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'ActivityHeatmap', | ||||
|   components: {CalendarHeatmap}, | ||||
|   props: { | ||||
|     values: { | ||||
|       type: Array, | ||||
|       default: () => [], | ||||
|     }, | ||||
|   }, | ||||
|   data: () => ({ | ||||
|     isLoading: true, | ||||
|     colorRange: [ | ||||
|       'var(--color-secondary-alpha-70)', | ||||
|       'var(--color-primary-light-4)', | ||||
|  | @ -35,20 +35,11 @@ export default { | |||
|       'var(--color-primary-dark-4)', | ||||
|     ], | ||||
|     endDate: new Date(), | ||||
|     values: [], | ||||
|     locale: { | ||||
|       contributions: 'contributions', | ||||
|       no_contributions: 'No contributions', | ||||
|     }, | ||||
|   }), | ||||
|   async mounted() { | ||||
|     const res = await fetch(`${AppSubUrl}/api/v1/users/${heatmapUser}/heatmap`); | ||||
|     const data = await res.json(); | ||||
|     this.values = data.map(({contributions, timestamp}) => { | ||||
|       return {date: new Date(timestamp * 1000), count: contributions}; | ||||
|     }); | ||||
|     this.isLoading = false; | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <style scoped/> | ||||
|  |  | |||
							
								
								
									
										23
									
								
								web_src/js/features/heatmap.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								web_src/js/features/heatmap.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | |||
| import Vue from 'vue'; | ||||
| 
 | ||||
| import ActivityHeatmap from '../components/ActivityHeatmap.vue'; | ||||
| 
 | ||||
| export default async function initHeatmap() { | ||||
|   const el = document.getElementById('user-heatmap'); | ||||
|   if (!el) return; | ||||
| 
 | ||||
|   try { | ||||
|     const values = JSON.parse(el.dataset.heatmapData).map(({contributions, timestamp}) => { | ||||
|       return {date: new Date(timestamp * 1000), count: contributions}; | ||||
|     }); | ||||
| 
 | ||||
|     const View = Vue.extend({ | ||||
|       render: (createElement) => createElement(ActivityHeatmap, {props: {values}}), | ||||
|     }); | ||||
| 
 | ||||
|     new View().$mount(el); | ||||
|   } catch (err) { | ||||
|     console.error(err); | ||||
|     el.textContent = 'Heatmap failed to load'; | ||||
|   } | ||||
| } | ||||
|  | @ -1,10 +0,0 @@ | |||
| import Vue from 'vue'; | ||||
| 
 | ||||
| import ActivityHeatmap from '../components/ActivityHeatmap.vue'; | ||||
| 
 | ||||
| export default async function initUserHeatmap() { | ||||
|   const el = document.getElementById('user-heatmap'); | ||||
|   if (!el) return; | ||||
|   const View = Vue.extend(ActivityHeatmap); | ||||
|   new View().$mount(el); | ||||
| } | ||||
|  | @ -12,7 +12,7 @@ import initMigration from './features/migration.js'; | |||
| import initContextPopups from './features/contextpopup.js'; | ||||
| import initGitGraph from './features/gitgraph.js'; | ||||
| import initClipboard from './features/clipboard.js'; | ||||
| import initUserHeatmap from './features/userheatmap.js'; | ||||
| import initHeatmap from './features/heatmap.js'; | ||||
| import initProject from './features/projects.js'; | ||||
| import initServiceWorker from './features/serviceworker.js'; | ||||
| import initMarkdownAnchors from './markdown/anchors.js'; | ||||
|  | @ -2553,7 +2553,7 @@ $(document).ready(async () => { | |||
|     attachTribute(document.querySelectorAll('#content, .emoji-input')), | ||||
|     initGitGraph(), | ||||
|     initClipboard(), | ||||
|     initUserHeatmap(), | ||||
|     initHeatmap(), | ||||
|     initProject(), | ||||
|     initServiceWorker(), | ||||
|     initNotificationCount(), | ||||
|  |  | |||
|  | @ -1,49 +1,50 @@ | |||
| #user-heatmap { | ||||
|   width: 107%; // Fixes newest contributions not showing | ||||
|   width: 100%; | ||||
|   text-align: center; | ||||
|   position: relative; | ||||
|   min-height: 125px; | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   justify-content: center; | ||||
| 
 | ||||
|   svg:not(:root) { | ||||
|     overflow: inherit; | ||||
|     padding: 0 !important; | ||||
|   } | ||||
| 
 | ||||
|   text { | ||||
|     fill: currentColor !important; | ||||
|   } | ||||
| 
 | ||||
|   @media @mediaLgAndDown { | ||||
|     & { | ||||
|     &, | ||||
|     & + .divider { | ||||
|       display: none; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .total-contributions { | ||||
|     text-align: left; | ||||
|     margin-top: 0; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .heatmap-container { | ||||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
| .heatmap-container .total-contributions { | ||||
|     font-size: 11px; | ||||
|     position: absolute; | ||||
|     bottom: 0; | ||||
|     left: 25px; | ||||
| } | ||||
|   } | ||||
| 
 | ||||
| @media @mediaLgAndDown { | ||||
|   .heatmap-container .total-contributions { | ||||
|   @media @mediaLgAndDown { | ||||
|     .total-contributions { | ||||
|       left: 21px; | ||||
|     } | ||||
| } | ||||
|   } | ||||
| 
 | ||||
| @media (max-width: 1000px) { | ||||
|   .heatmap-container .total-contributions { | ||||
|   @media (max-width: 1000px) { | ||||
|     .total-contributions { | ||||
|       font-size: 10px; | ||||
|       left: 17px; | ||||
|       bottom: -2px; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .heatmap-container text { | ||||
|   fill: currentColor !important; | ||||
| .user.profile #user-heatmap { | ||||
|   min-height: 135px; | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue