forked from cybrespace/pinafore
implement hashtags
This commit is contained in:
parent
0460a7de2d
commit
a11f31bb3f
|
@ -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
|
||||
|
|
|
@ -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…
Reference in New Issue