show number of favs/boosts and favs/boosts page
This commit is contained in:
parent
e7fc226935
commit
de34efc554
19
routes/_api/reblogsAndFavs.js
Normal file
19
routes/_api/reblogsAndFavs.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { get, paramsString } from '../_utils/ajax'
|
||||
|
||||
// TODO: paginate
|
||||
|
||||
export async function getReblogs(instanceName, accessToken, statusId, limit = 80) {
|
||||
let url = `https://${instanceName}/api/v1/statuses/${statusId}/reblogged_by`
|
||||
url += '?' + paramsString({ limit })
|
||||
return get(url, {
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
})
|
||||
}
|
||||
|
||||
export async function getFavorites(instanceName, accessToken, statusId, limit = 80) {
|
||||
let url = `https://${instanceName}/api/v1/statuses/${statusId}/favourited_by`
|
||||
url += '?' + paramsString({ limit })
|
||||
return get(url, {
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
})
|
||||
}
|
62
routes/_components/AccountsListPage.html
Normal file
62
routes/_components/AccountsListPage.html
Normal file
|
@ -0,0 +1,62 @@
|
|||
<div class="accounts-page">
|
||||
{{#if loading}}
|
||||
<LoadingPage />
|
||||
{{elseif accounts && accounts.length}}
|
||||
<ul class="accounts-results">
|
||||
{{#each accounts as account}}
|
||||
<AccountSearchResult :account />
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
<style>
|
||||
.accounts-page {
|
||||
min-height: 60vh;
|
||||
padding: 20px 20px;
|
||||
}
|
||||
.accounts-results {
|
||||
list-style: none;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--main-border);
|
||||
border-radius: 2px;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.accounts-page {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import Layout from '../_components/Layout.html'
|
||||
import { store } from '../_store/store'
|
||||
import LoadingPage from '../_components/LoadingPage.html'
|
||||
import AccountSearchResult from '../_components/search/AccountSearchResult.html'
|
||||
import { toast } from '../_utils/toast'
|
||||
|
||||
export default {
|
||||
async oncreate() {
|
||||
let accountsFetcher = this.get('accountsFetcher')
|
||||
let statusId = this.get('statusId')
|
||||
let instanceName = this.store.get('currentInstance')
|
||||
let accessToken = this.store.get('accessToken')
|
||||
try {
|
||||
let accounts = await accountsFetcher(instanceName, accessToken, statusId)
|
||||
this.set({ accounts: accounts })
|
||||
} catch (e) {
|
||||
toast.say('Error: ' + (e.name || '') + ' ' + (e.message || ''))
|
||||
} finally {
|
||||
this.set({loading: false})
|
||||
}
|
||||
},
|
||||
data: () => ({
|
||||
loading: true,
|
||||
accounts: []
|
||||
}),
|
||||
store: () => store,
|
||||
components: {
|
||||
Layout,
|
||||
LoadingPage,
|
||||
AccountSearchResult
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -26,7 +26,7 @@
|
|||
<StatusMediaAttachments status="{{originalStatus}}" :contextualStatusId on:recalculateHeight />
|
||||
{{/if}}
|
||||
{{#if isStatusInOwnThread}}
|
||||
<StatusAbsoluteDate status="{{originalStatus}}" />
|
||||
<StatusDetails status="{{originalStatus}}" />
|
||||
{{/if}}
|
||||
<StatusToolbar :status :isStatusInOwnThread />
|
||||
</article>
|
||||
|
@ -65,7 +65,7 @@
|
|||
"spoiler-btn spoiler-btn"
|
||||
"content content"
|
||||
"media media"
|
||||
"date date"
|
||||
"details details"
|
||||
"toolbar toolbar";
|
||||
grid-template-columns: min-content 1fr;
|
||||
}
|
||||
|
@ -83,7 +83,7 @@
|
|||
import StatusAuthorName from './StatusAuthorName.html'
|
||||
import StatusAuthorHandle from './StatusAuthorHandle.html'
|
||||
import StatusRelativeDate from './StatusRelativeDate.html'
|
||||
import StatusAbsoluteDate from './StatusAbsoluteDate.html'
|
||||
import StatusDetails from './StatusDetails.html'
|
||||
import StatusToolbar from './StatusToolbar.html'
|
||||
import StatusMediaAttachments from './StatusMediaAttachments.html'
|
||||
import StatusContent from './StatusContent.html'
|
||||
|
@ -116,7 +116,7 @@
|
|||
StatusAuthorName,
|
||||
StatusAuthorHandle,
|
||||
StatusRelativeDate,
|
||||
StatusAbsoluteDate,
|
||||
StatusDetails,
|
||||
StatusToolbar,
|
||||
StatusMediaAttachments,
|
||||
StatusContent,
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<ExternalLink class="status-absolute-date" href="{{status.url}}" showIcon="true">
|
||||
<time datetime={{createdAtDate}} title="{{formattedDate}}">{{formattedDate}}</time>
|
||||
</ExternalLink>
|
||||
<style>
|
||||
:global(.status-absolute-date) {
|
||||
grid-area: date;
|
||||
font-size: 1.1em;
|
||||
white-space: nowrap;
|
||||
margin: 0 5px 10px;
|
||||
justify-self: right;
|
||||
}
|
||||
|
||||
:global(.status-absolute-date, .status-absolute-date:hover, .status-absolute-date:visited) {
|
||||
color: var(--deemphasized-text-color);
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import ExternalLink from '../ExternalLink.html'
|
||||
|
||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
createdAtDate: (status) => status.created_at,
|
||||
formattedDate: (createdAtDate) => formatter.format(new Date(createdAtDate))
|
||||
},
|
||||
components: {
|
||||
ExternalLink
|
||||
}
|
||||
}
|
||||
</script>
|
104
routes/_components/status/StatusDetails.html
Normal file
104
routes/_components/status/StatusDetails.html
Normal file
|
@ -0,0 +1,104 @@
|
|||
<div class="status-details">
|
||||
<ExternalLink class="status-absolute-date" href="{{status.url}}" showIcon="true">
|
||||
<time datetime={{createdAtDate}} title="{{formattedDate}}">{{formattedDate}}</time>
|
||||
</ExternalLink>
|
||||
<a class="status-favs-reblogs" href="/statuses/{{statusId}}/reblogs">
|
||||
<svg>
|
||||
<use xlink:href="#fa-retweet"/>
|
||||
</svg>
|
||||
<span>{{numReblogs}}</span>
|
||||
</a>
|
||||
<a class="status-favs-reblogs" href="/statuses/{{statusId}}/favorites">
|
||||
<svg>
|
||||
<use xlink:href="#fa-star" />
|
||||
</svg>
|
||||
<span>{{numFavs}}</span>
|
||||
</a>
|
||||
</div>
|
||||
<style>
|
||||
.status-details {
|
||||
grid-area: details;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, max-content) min-content min-content;
|
||||
grid-gap: 20px;
|
||||
align-items: center;
|
||||
justify-content: left;
|
||||
margin: 0 5px 10px;
|
||||
}
|
||||
:global(.status-absolute-date) {
|
||||
font-size: 1.1em;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
:global(.status-absolute-date time) {
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.status-favs-reblogs {
|
||||
font-size: 1.1em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-favs-reblogs span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.status-favs-reblogs,
|
||||
.status-favs-reblogs:hover,
|
||||
.status-favs-reblogs:visited {
|
||||
color: var(--deemphasized-text-color);
|
||||
}
|
||||
|
||||
.status-favs-reblogs svg {
|
||||
fill: var(--deemphasized-text-color);
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
:global(.status-absolute-date, .status-absolute-date:hover, .status-absolute-date:visited) {
|
||||
color: var(--deemphasized-text-color);
|
||||
}
|
||||
|
||||
@media (max-width: 479px) {
|
||||
:global(.status-absolute-date) {
|
||||
font-size: 1em;
|
||||
}
|
||||
.status-favs-reblogs {
|
||||
font-size: 1em;
|
||||
}
|
||||
.status-details {
|
||||
grid-gap: 5px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
import ExternalLink from '../ExternalLink.html'
|
||||
|
||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
statusId: (status) => status.id,
|
||||
createdAtDate: (status) => status.created_at,
|
||||
numReblogs: (status) => status.reblogs_count || 0,
|
||||
numFavs: (status) => status.favourites_count || 0,
|
||||
formattedDate: (createdAtDate) => formatter.format(new Date(createdAtDate))
|
||||
},
|
||||
components: {
|
||||
ExternalLink
|
||||
}
|
||||
}
|
||||
</script>
|
25
routes/statuses/[statusId]/favorites.html
Normal file
25
routes/statuses/[statusId]/favorites.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<:Head>
|
||||
<title>Pinafore – Favorites</title>
|
||||
</:Head>
|
||||
|
||||
<Layout page='favorites'>
|
||||
<AccountsListPage :accountsFetcher :statusId />
|
||||
</Layout>
|
||||
<script>
|
||||
import Layout from '../../_components/Layout.html'
|
||||
import { getFavorites } from '../../_api/reblogsAndFavs'
|
||||
import AccountsListPage from '../../_components/AccountsListPage.html'
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
accountsFetcher: getFavorites
|
||||
}),
|
||||
computed: {
|
||||
statusId: params => params.statusId
|
||||
},
|
||||
components: {
|
||||
Layout,
|
||||
AccountsListPage
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -17,12 +17,12 @@
|
|||
{{/if}}
|
||||
</Layout>
|
||||
<script>
|
||||
import Layout from '../_components/Layout.html'
|
||||
import LazyTimeline from '../_components/timeline/LazyTimeline.html'
|
||||
import FreeTextLayout from '../_components/FreeTextLayout.html'
|
||||
import { store } from '../_store/store.js'
|
||||
import HiddenFromSSR from '../_components/HiddenFromSSR'
|
||||
import DynamicPageBanner from '../_components/DynamicPageBanner.html'
|
||||
import Layout from '../../_components/Layout.html'
|
||||
import LazyTimeline from '../../_components/timeline/LazyTimeline.html'
|
||||
import FreeTextLayout from '../../_components/FreeTextLayout.html'
|
||||
import { store } from '../../_store/store.js'
|
||||
import HiddenFromSSR from '../../_components/HiddenFromSSR'
|
||||
import DynamicPageBanner from '../../_components/DynamicPageBanner.html'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
25
routes/statuses/[statusId]/reblogs.html
Normal file
25
routes/statuses/[statusId]/reblogs.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<:Head>
|
||||
<title>Pinafore – Reblogs</title>
|
||||
</:Head>
|
||||
|
||||
<Layout page='reblogs'>
|
||||
<AccountsListPage :accountsFetcher :statusId />
|
||||
</Layout>
|
||||
<script>
|
||||
import Layout from '../../_components/Layout.html'
|
||||
import { getReblogs } from '../../_api/reblogsAndFavs'
|
||||
import AccountsListPage from '../../_components/AccountsListPage.html'
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
accountsFetcher: getReblogs
|
||||
}),
|
||||
computed: {
|
||||
statusId: params => params.statusId
|
||||
},
|
||||
components: {
|
||||
Layout,
|
||||
AccountsListPage
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Reference in a new issue