add spoiler text support
This commit is contained in:
parent
18ad6ab1ab
commit
6790cfd187
|
@ -5,20 +5,20 @@
|
|||
<button type="button"
|
||||
class="play-video-button"
|
||||
aria-label="Play video"
|
||||
on:click="onClickPlayVideoButton(media, getSmallWidth(media), getSmallHeight(media))">
|
||||
on:click="onClickPlayVideoButton(media, getSmallWidth(media), getSmallHeight(media), media.description)">
|
||||
<div class="svg-wrapper">
|
||||
<svg>
|
||||
<use xlink:href="#fa-play-circle" />
|
||||
</svg>
|
||||
</div>
|
||||
<img aria-hidden="true"
|
||||
alt=""
|
||||
<img alt="{{media.description || ''}}"
|
||||
src="{{media.preview_url}}"
|
||||
width="{{getSmallWidth(media)}}"
|
||||
height="{{getSmallHeight(media)}}" />
|
||||
</button>
|
||||
{{elseif media.type === 'gifv'}}
|
||||
<video
|
||||
aria-label="Animated GIF: {{media.description || ''}}"
|
||||
poster="{{media.preview_url}}"
|
||||
src="{{media.url}}"
|
||||
width="{{getSmallWidth(media)}}"
|
||||
|
@ -29,8 +29,8 @@
|
|||
playsinline
|
||||
/>
|
||||
{{else}}
|
||||
<img src="{{media.preview_url}}"
|
||||
alt="{{media.description || ''}}"
|
||||
<img alt="{{media.description || ''}}"
|
||||
src="{{media.preview_url}}"
|
||||
width="{{getSmallWidth(media)}}"
|
||||
height="{{getSmallHeight(media)}}"/>
|
||||
{{/if}}
|
||||
|
@ -112,8 +112,8 @@
|
|||
minMediaWidth: (mediaAttachments) => Math.min.apply(Math, mediaAttachments.map(media => media.meta && media.meta.small && typeof media.meta.small.width === 'number' ? media.meta.small.width : DEFAULT_MEDIA_WIDTH))
|
||||
},
|
||||
methods: {
|
||||
async onClickPlayVideoButton(media, width, height) {
|
||||
showVideoDialog(media.preview_url, media.url, width, height)
|
||||
async onClickPlayVideoButton(media, width, height, description) {
|
||||
showVideoDialog(media.preview_url, media.url, width, height, description)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,41 @@
|
|||
<article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}">
|
||||
{{#if status.reblog}}
|
||||
<div class="status-boosted">
|
||||
<svg>
|
||||
<use xlink:href="#fa-retweet" />
|
||||
</svg>
|
||||
<span>
|
||||
<a href="/accounts/{{status.account.id}}">
|
||||
{{status.account.username}}
|
||||
</a> boosted
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}" on:recalculateHeight>
|
||||
{{#if status.reblog}}
|
||||
<div class="status-boosted">
|
||||
<svg>
|
||||
<use xlink:href="#fa-retweet" />
|
||||
</svg>
|
||||
<span>
|
||||
<a href="/accounts/{{status.account.id}}">
|
||||
{{status.account.username}}
|
||||
</a> boosted
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="status-author">
|
||||
<a class="status-author-name" href="/accounts/{{originalAccount.id}}">
|
||||
{{originalAccount.display_name || originalAccount.username}}
|
||||
</a>
|
||||
<span class="status-author-handle">
|
||||
@{{originalAccount.acct}}
|
||||
{{'@' + originalAccount.acct}}
|
||||
</span>
|
||||
<a class="status-author-date" rel="noopener" target="_blank" href="{{originalStatus.uri}}">
|
||||
<time datetime={{createdAtDate}} title="{{relativeDate}}">{{relativeDate}}</time>
|
||||
</a>
|
||||
</div>
|
||||
<Avatar account={{originalAccount}} className="status-sidebar"/>
|
||||
<div class="status-content">{{{status.content}}}</div>
|
||||
{{#if status.spoiler_text}}
|
||||
<div class="status-spoiler">{{status.spoiler_text}}</div>
|
||||
{{/if}}
|
||||
{{#if status.spoiler_text}}
|
||||
<div class="status-spoiler-button">
|
||||
<button type="button" on:click="onClickSpoilerButton()">Show more</button>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if !status.spoiler_text || spoilerShown}}
|
||||
<div class="status-content">
|
||||
{{{status.content}}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<Toolbar :status />
|
||||
<Media mediaAttachments="{{originalMediaAttachments}}" />
|
||||
</article>
|
||||
|
@ -37,6 +49,8 @@
|
|||
grid-template-areas:
|
||||
".............. status-boosted"
|
||||
"status-sidebar status-author"
|
||||
"status-sidebar status-spoiler"
|
||||
"status-sidebar status-spoiler-button"
|
||||
"status-sidebar status-content"
|
||||
".............. status-toolbar"
|
||||
"status-media status-media";
|
||||
|
@ -50,6 +64,25 @@
|
|||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.status-spoiler {
|
||||
grid-area: status-spoiler;
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
white-space: pre-wrap;
|
||||
font-size: 1.1em;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.status-spoiler-button {
|
||||
grid-area: status-spoiler-button;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.status-spoiler-button button {
|
||||
padding: 5px 10px;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.status-author {
|
||||
grid-area: status-author;
|
||||
display: flex;
|
||||
|
@ -94,7 +127,7 @@
|
|||
}
|
||||
|
||||
.status-content {
|
||||
margin: 10px 10px 20px 5px;
|
||||
margin: 10px 10px 10px 5px;
|
||||
grid-area: status-content;
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
|
@ -160,6 +193,12 @@
|
|||
originalStatus: (status) => status.reblog ? status.reblog : status,
|
||||
originalAccount: (originalStatus) => originalStatus.account,
|
||||
originalMediaAttachments: (originalStatus) => originalStatus.media_attachments,
|
||||
},
|
||||
methods: {
|
||||
onClickSpoilerButton() {
|
||||
this.set({spoilerShown: !this.get('spoilerShown')})
|
||||
this.fire('recalculateHeight')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,4 +1,4 @@
|
|||
<Status status="{{virtualProps}}" index="{{virtualIndex}}" length="{{virtualLength}}"/>
|
||||
<Status status="{{virtualProps}}" index="{{virtualIndex}}" length="{{virtualLength}}" on:recalculateHeight />
|
||||
<script>
|
||||
import Status from './Status.html'
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
src="{{src}}"
|
||||
width="{{width}}"
|
||||
height="{{height}}"
|
||||
aria-label="Video: {{description || ''}}"
|
||||
controls
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
<div class="virtual-list-item {{shown ? 'shown' : ''}}"
|
||||
virtual-list-key="{{key}}"
|
||||
ref:node
|
||||
style="transform: translateY({{offset}}px
|
||||
);"
|
||||
>
|
||||
<:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$numItems}}"/>
|
||||
style="transform: translateY({{offset}}px);" >
|
||||
<:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$numItems}}"
|
||||
on:recalculateHeight="doRecalculateHeight()"/>
|
||||
</div>
|
||||
<style>
|
||||
.virtual-list-item {
|
||||
|
@ -23,7 +22,8 @@
|
|||
import { virtualListStore } from '../_utils/virtualListStore'
|
||||
import { AsyncLayout } from '../_utils/AsyncLayout'
|
||||
|
||||
const asyncLayout = new AsyncLayout(node => node.getAttribute('virtual-list-key'))
|
||||
const keyGetter = node => node.getAttribute('virtual-list-key')
|
||||
const asyncLayout = new AsyncLayout(keyGetter)
|
||||
|
||||
export default {
|
||||
oncreate() {
|
||||
|
@ -40,6 +40,17 @@
|
|||
store: () => virtualListStore,
|
||||
computed: {
|
||||
'shown': ($itemHeights, key) => $itemHeights[key] > 0
|
||||
},
|
||||
methods: {
|
||||
doRecalculateHeight() {
|
||||
let tempAsyncLayout = new AsyncLayout(keyGetter)
|
||||
let key = this.get('key')
|
||||
tempAsyncLayout.observe(key, this.refs.node, (rect) => {
|
||||
tempAsyncLayout.disconnect()
|
||||
// update all item heights in one microtask batch for better perf
|
||||
this.store.batchUpdate('itemHeights', key, rect.height)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -26,6 +26,11 @@ class AsyncLayout {
|
|||
this._intersectionObserver.unobserve(node)
|
||||
delete this._onIntersectionCallbacks[key]
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this._intersectionObserver.disconnect()
|
||||
this._intersectionObserver = null
|
||||
}
|
||||
}
|
||||
|
||||
export { AsyncLayout }
|
|
@ -1,6 +1,6 @@
|
|||
import VideoDialog from '../_components/VideoDialog.html'
|
||||
|
||||
export function showVideoDialog(poster, src, width, height) {
|
||||
export function showVideoDialog(poster, src, width, height, description) {
|
||||
let dialog = document.createElement('dialog')
|
||||
dialog.classList.add('video-dialog')
|
||||
dialog.setAttribute('aria-label', 'Video dialog')
|
||||
|
@ -12,7 +12,8 @@ export function showVideoDialog(poster, src, width, height) {
|
|||
src: src,
|
||||
dialog: dialog,
|
||||
width: width,
|
||||
height: height
|
||||
height: height,
|
||||
description: description
|
||||
}
|
||||
})
|
||||
videoDialog.showModal()
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
{{#if instanceUserAccount}}
|
||||
<h2>Logged in as:</h2>
|
||||
<div class="acct-current-user">
|
||||
<img alt="Profile picture for @{{instanceUserAccount.acct}}"
|
||||
<img alt="Profile picture for {{'@' + instanceUserAccount.acct}}"
|
||||
class="acct-avatar" src="{{instanceUserAccount.avatar}}" />
|
||||
<a class="acct-handle" rel="noopener" target="_blank"
|
||||
href="{{instanceUserAccount.url}}">@{{instanceUserAccount.acct}}</a>
|
||||
href="{{instanceUserAccount.url}}">{{'@' + instanceUserAccount.acct}}</a>
|
||||
<span class="acct-display-name">{{instanceUserAccount.display_name}}</span>
|
||||
</div>
|
||||
<h2>Theme:</h2>
|
||||
|
|
Loading…
Reference in a new issue