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