implement hashtags
This commit is contained in:
		
							parent
							
								
									0460a7de2d
								
							
						
					
					
						commit
						a11f31bb3f
					
				
					 8 changed files with 126 additions and 7 deletions
				
			
		|  | @ -1,5 +1,5 @@ | |||
| <:Window bind:innerHeight /> | ||||
| <Nav page={{page}} /> | ||||
| <Nav :page :dynamicPage :dynamicHref :dynamicIcon :dynamicLabel/> | ||||
| 
 | ||||
| <div class="container" on:scroll="onScroll(event)" on:fullscreen="onFullscreenChange()" ref:node> | ||||
|   <main> | ||||
|  |  | |||
|  | @ -15,6 +15,12 @@ | |||
| 	  <li> | ||||
|       <NavItem :page name="settings" href="/settings" svg="#fa-gears" label="Settings" /> | ||||
| 		</li> | ||||
|     {{#if dynamicPage}} | ||||
|     <li> | ||||
|       <NavItem :page name="{{dynamicPage}}" href="{{dynamicHref}}" svg="{{dynamicIcon}}" | ||||
|                label="{{dynamicLabel}}" forceCurrent="true" /> | ||||
|     </li> | ||||
|     {{/if}} | ||||
| 	</ul> | ||||
| </nav> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <a class='main-nav-link {{page === name ? "selected" : ""}}' | ||||
|    aria-label='{{page === name ? `${label} (current page)` : label}}' | ||||
|    aria-current="{{page === name}}" | ||||
| <a class='main-nav-link {{forceCurrent || page === name ? "selected" : ""}}' | ||||
|    aria-label='{{forceCurrent || page === name ? `${label} (current page)` : label}}' | ||||
|    aria-current="{{forceCurrent || page === name}}" | ||||
|    href='{{href}}'> | ||||
|   <svg> | ||||
|     <use xlink:href="{{svg}}" /> | ||||
|  |  | |||
|  | @ -32,7 +32,7 @@ | |||
|     </div> | ||||
|   {{/if}} | ||||
|   {{#if !status.spoiler_text || spoilerShown}} | ||||
|     <div class="status-content"> | ||||
|     <div class="status-content" ref:contentNode> | ||||
|       {{{status.content}}} | ||||
|     </div> | ||||
|   {{/if}} | ||||
|  | @ -286,6 +286,9 @@ | |||
|   const relativeFormat = new IntlRelativeFormat('en-US'); | ||||
| 
 | ||||
|   export default { | ||||
|     oncreate() { | ||||
|       this.hydrateHtml() | ||||
|     }, | ||||
|     components: { | ||||
|       Avatar, | ||||
|       Media, | ||||
|  | @ -306,11 +309,26 @@ | |||
|     methods: { | ||||
|       onClickSpoilerButton() { | ||||
|         this.set({spoilerShown: !this.get('spoilerShown')}) | ||||
|         this.hydrateHtml() | ||||
|         this.fire('recalculateHeight') | ||||
|       }, | ||||
|       onClickSensitiveMediaButton() { | ||||
|         this.set({sensitiveShown: !this.get('sensitiveShown')}) | ||||
|         this.fire('recalculateHeight') | ||||
|       }, | ||||
|       hydrateHtml() { | ||||
|         let status = this.get('originalStatus') | ||||
|         if (status.tags && status.tags.length && this.refs.contentNode) { | ||||
|           let anchorTags = this.refs.contentNode.querySelectorAll('a[rel=tag]') | ||||
|           for (let tag of status.tags) { | ||||
|             let {name, url} = tag | ||||
|             for (let anchorTag of anchorTags) { | ||||
|               if (anchorTag.getAttribute('href') === url) { | ||||
|                 anchorTag.setAttribute('href', `/tags/${name}`) | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  |  | |||
|  | @ -52,7 +52,14 @@ | |||
|         key: status.id | ||||
|       })), | ||||
|       lastStatusId: (statuses) => statuses.length && statuses[statuses.length - 1].id, | ||||
|       label: (timeline, $currentInstance) => `${timelines[timeline].label} timeline for ${$currentInstance}` | ||||
|       label: (timeline, $currentInstance) => { | ||||
|         if (timelines[timeline]) { | ||||
|           `${timelines[timeline].label} timeline for ${$currentInstance}` | ||||
|         } else if (timeline.startsWith('tag/')) { | ||||
|           let tag = timeline.split('/').slice(-1)[0] | ||||
|           return `#${tag} timeline for ${$currentInstance}` | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     store: () => store, | ||||
|     components: { | ||||
|  |  | |||
|  | @ -1,10 +1,26 @@ | |||
| import { get, paramsString } from '../ajax' | ||||
| import { basename } from './utils' | ||||
| 
 | ||||
| function getTimelineUrlName(timeline) { | ||||
|   switch (timeline) { | ||||
|     case 'local': | ||||
|     case 'federated': | ||||
|       return 'public' | ||||
|     case 'home': | ||||
|       return 'home' | ||||
|     default: | ||||
|       return 'tag' | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function getTimeline(instanceName, accessToken, timeline, maxId, since) { | ||||
|   let timelineUrlName = timeline === 'local' || timeline === 'federated' ?  'public' : timeline | ||||
|   let timelineUrlName = getTimelineUrlName(timeline) | ||||
|   let url = `${basename(instanceName)}/api/v1/timelines/${timelineUrlName}` | ||||
| 
 | ||||
|   if (timeline.startsWith('tag/')) { | ||||
|     url += '/' + timeline.split('/').slice(-1)[0] | ||||
|   } | ||||
| 
 | ||||
|   let params = {} | ||||
|   if (since) { | ||||
|     params.since = since | ||||
|  |  | |||
							
								
								
									
										66
									
								
								routes/tags/[tagName].html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								routes/tags/[tagName].html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | |||
| <:Head> | ||||
|   <title>Pinafore – #{{params.tagName}}</title> | ||||
| </:Head> | ||||
| 
 | ||||
| <Layout page='tags' | ||||
|         dynamicPage="{{params.tagName}}" | ||||
|         dynamicHref="/tags/{{params.tagName}}" | ||||
|         dynamicLabel="{{'#' + params.tagName}}" | ||||
|         dynamicIcon="#fa-hashtag" > | ||||
|   {{#if $isUserLoggedIn}} | ||||
|   <div class="hashtag-banner"> | ||||
|     <h1 class="hashtag-title">{{'#' + params.tagName}}</h1> | ||||
|     <a href="#" class="hashtag-go-back" on:click="onGoBack(event)">Back</a> | ||||
|   </div> | ||||
|   <LazyTimeline timeline='tag/{{params.tagName}}' /> | ||||
|   {{else}} | ||||
|   <HiddenFromSSR> | ||||
|     <FreeTextLayout> | ||||
|       <h1>#{{params.tagName}}</h1> | ||||
| 
 | ||||
|       <p>A hashtag timeline will appear here when logged in.</p> | ||||
|     </FreeTextLayout> | ||||
|   </HiddenFromSSR> | ||||
|   {{/if}} | ||||
| </Layout> | ||||
| <style> | ||||
|   .hashtag-banner { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: space-between; | ||||
|     margin: 0 20px 20px; | ||||
|   } | ||||
|   h1.hashtag-title { | ||||
|     margin: 0; | ||||
|   } | ||||
|   a.hashtag-go-back { | ||||
|     font-size: 1.3em; | ||||
|   } | ||||
|   a.hashtag-go-back::before { | ||||
|     content: '<'; | ||||
|     margin-right: 5px; | ||||
|   } | ||||
| </style> | ||||
| <script> | ||||
|   import Layout from '../_components/Layout.html' | ||||
|   import LazyTimeline from '../_components/LazyTimeline.html' | ||||
|   import FreeTextLayout from '../_components/FreeTextLayout.html' | ||||
|   import { store } from '../_utils/store.js' | ||||
|   import HiddenFromSSR from '../_components/HiddenFromSSR' | ||||
| 
 | ||||
|   export default { | ||||
|     store: () => store, | ||||
|     components: { | ||||
|       Layout, | ||||
|       LazyTimeline, | ||||
|       FreeTextLayout, | ||||
|       HiddenFromSSR | ||||
|     }, | ||||
|     methods: { | ||||
|       onGoBack(e) { | ||||
|         e.preventDefault() | ||||
|         window.history.back(); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | @ -134,6 +134,12 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o | |||
|       <path d="M555 1335l78-141q-87-63-136-159t-49-203q0-121 61-225-229 117-381 353 167 258 427 375zm389-759q0-20-14-34t-34-14q-125 0-214.5 89.5T592 832q0 20 14 34t34 14 34-14 14-34q0-86 61-147t147-61q20 0 34-14t14-34zm363-191q0 7-1 9-106 189-316 567t-315 566l-49 89q-10 16-28 16-12 0-134-70-16-10-16-28 0-12 44-87-143-65-263.5-173T20 1029Q0 998 0 960t20-69q153-235 380-371t496-136q89 0 180 17l54-97q10-16 28-16 5 0 18 6t31 15.5 33 18.5 31.5 18.5T1291 358q16 10 16 27zm37 447q0 139-79 253.5T1056 1250l280-502q8 45 8 84zm448 128q0 35-20 69-39 64-109 145-150 172-347.5 267T896 1536l74-132q212-18 392.5-137T1664 960q-115-179-282-294l63-112q95 64 182.5 153T1772 891q20 34 20 69z"/> | ||||
|     </symbol> | ||||
| 
 | ||||
|     <symbol id="fa-hashtag" viewBox="0 0 1792 1792"> | ||||
|       <title>Hashtag</title> | ||||
|       <path d="M991 1024l64-256H801l-64 256h254zm768-504l-56 224q-7 24-31 24h-327l-64 256h311q15 0 25 12 10 14 6 28l-56 224q-5 24-31 24h-327l-81 328q-7 24-31 24H873q-16 0-26-12-9-12-6-28l78-312H665l-81 328q-7 24-31 24H328q-15 0-25-12-9-12-6-28l78-312H64q-15 0-25-12-9-12-6-28l56-224q7-24 31-24h327l64-256H200q-15 0-25-12-10-14-6-28l56-224q5-24 31-24h327l81-328q7-24 32-24h224q15 0 25 12 9 12 6 28l-78 312h254l81-328q7-24 32-24h224q15 0 25 12 9 12 6 28l-78 312h311q15 0 25 12 9 12 6 28z"/> | ||||
|     </symbol> | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   </svg> | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue