implement hashtags
This commit is contained in:
parent
0460a7de2d
commit
a11f31bb3f
|
@ -1,5 +1,5 @@
|
||||||
<:Window bind:innerHeight />
|
<:Window bind:innerHeight />
|
||||||
<Nav page={{page}} />
|
<Nav :page :dynamicPage :dynamicHref :dynamicIcon :dynamicLabel/>
|
||||||
|
|
||||||
<div class="container" on:scroll="onScroll(event)" on:fullscreen="onFullscreenChange()" ref:node>
|
<div class="container" on:scroll="onScroll(event)" on:fullscreen="onFullscreenChange()" ref:node>
|
||||||
<main>
|
<main>
|
||||||
|
|
|
@ -15,6 +15,12 @@
|
||||||
<li>
|
<li>
|
||||||
<NavItem :page name="settings" href="/settings" svg="#fa-gears" label="Settings" />
|
<NavItem :page name="settings" href="/settings" svg="#fa-gears" label="Settings" />
|
||||||
</li>
|
</li>
|
||||||
|
{{#if dynamicPage}}
|
||||||
|
<li>
|
||||||
|
<NavItem :page name="{{dynamicPage}}" href="{{dynamicHref}}" svg="{{dynamicIcon}}"
|
||||||
|
label="{{dynamicLabel}}" forceCurrent="true" />
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<a class='main-nav-link {{page === name ? "selected" : ""}}'
|
<a class='main-nav-link {{forceCurrent || page === name ? "selected" : ""}}'
|
||||||
aria-label='{{page === name ? `${label} (current page)` : label}}'
|
aria-label='{{forceCurrent || page === name ? `${label} (current page)` : label}}'
|
||||||
aria-current="{{page === name}}"
|
aria-current="{{forceCurrent || page === name}}"
|
||||||
href='{{href}}'>
|
href='{{href}}'>
|
||||||
<svg>
|
<svg>
|
||||||
<use xlink:href="{{svg}}" />
|
<use xlink:href="{{svg}}" />
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if !status.spoiler_text || spoilerShown}}
|
{{#if !status.spoiler_text || spoilerShown}}
|
||||||
<div class="status-content">
|
<div class="status-content" ref:contentNode>
|
||||||
{{{status.content}}}
|
{{{status.content}}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -286,6 +286,9 @@
|
||||||
const relativeFormat = new IntlRelativeFormat('en-US');
|
const relativeFormat = new IntlRelativeFormat('en-US');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
oncreate() {
|
||||||
|
this.hydrateHtml()
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
Avatar,
|
Avatar,
|
||||||
Media,
|
Media,
|
||||||
|
@ -306,11 +309,26 @@
|
||||||
methods: {
|
methods: {
|
||||||
onClickSpoilerButton() {
|
onClickSpoilerButton() {
|
||||||
this.set({spoilerShown: !this.get('spoilerShown')})
|
this.set({spoilerShown: !this.get('spoilerShown')})
|
||||||
|
this.hydrateHtml()
|
||||||
this.fire('recalculateHeight')
|
this.fire('recalculateHeight')
|
||||||
},
|
},
|
||||||
onClickSensitiveMediaButton() {
|
onClickSensitiveMediaButton() {
|
||||||
this.set({sensitiveShown: !this.get('sensitiveShown')})
|
this.set({sensitiveShown: !this.get('sensitiveShown')})
|
||||||
this.fire('recalculateHeight')
|
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
|
key: status.id
|
||||||
})),
|
})),
|
||||||
lastStatusId: (statuses) => statuses.length && statuses[statuses.length - 1].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,
|
store: () => store,
|
||||||
components: {
|
components: {
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
import { get, paramsString } from '../ajax'
|
import { get, paramsString } from '../ajax'
|
||||||
import { basename } from './utils'
|
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) {
|
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}`
|
let url = `${basename(instanceName)}/api/v1/timelines/${timelineUrlName}`
|
||||||
|
|
||||||
|
if (timeline.startsWith('tag/')) {
|
||||||
|
url += '/' + timeline.split('/').slice(-1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
let params = {}
|
let params = {}
|
||||||
if (since) {
|
if (since) {
|
||||||
params.since = 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"/>
|
<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>
|
||||||
|
|
||||||
|
<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>
|
</svg>
|
||||||
|
|
Loading…
Reference in a new issue