add spoiler text support

This commit is contained in:
Nolan Lawson 2018-01-21 10:32:18 -08:00
parent 18ad6ab1ab
commit 6790cfd187
8 changed files with 90 additions and 33 deletions

View file

@ -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)
}
}
}

View file

@ -1,4 +1,4 @@
<article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}">
<article class="status-article" tabindex="0" aria-posinset="{{index}}" aria-setsize="{{length}}" on:recalculateHeight>
{{#if status.reblog}}
<div class="status-boosted">
<svg>
@ -16,14 +16,26 @@
{{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>

View file

@ -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'

View file

@ -8,6 +8,7 @@
src="{{src}}"
width="{{width}}"
height="{{height}}"
aria-label="Video: {{description || ''}}"
controls
/>
</div>

View file

@ -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>

View file

@ -26,6 +26,11 @@ class AsyncLayout {
this._intersectionObserver.unobserve(node)
delete this._onIntersectionCallbacks[key]
}
disconnect() {
this._intersectionObserver.disconnect()
this._intersectionObserver = null
}
}
export { AsyncLayout }

View file

@ -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()

View file

@ -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>