|
@@ -0,0 +1,1165 @@
|
|
|
+<template>
|
|
|
+ <div class="profile-feed-component">
|
|
|
+ <div class="profile-feed-component-nav d-flex justify-content-center justify-content-md-between align-items-center mb-4">
|
|
|
+ <div class="d-none d-md-block border-bottom flex-grow-1 profile-nav-btns">
|
|
|
+ <div class="btn-group">
|
|
|
+ <button
|
|
|
+ class="btn btn-link"
|
|
|
+ :class="[ tabIndex === 1 ? 'active' : '' ]"
|
|
|
+ @click="toggleTab(1)"
|
|
|
+ >
|
|
|
+ Posts
|
|
|
+ </button>
|
|
|
+ <!-- <button
|
|
|
+ class="btn btn-link"
|
|
|
+ :class="[ tabIndex === 3 ? 'text-dark font-weight-bold' : 'text-lighter' ]"
|
|
|
+ @click="toggleTab(3)">
|
|
|
+ Albums
|
|
|
+ </button> -->
|
|
|
+
|
|
|
+ <button
|
|
|
+ v-if="isOwner"
|
|
|
+ class="btn btn-link"
|
|
|
+ :class="[ tabIndex === 'archives' ? 'active' : '' ]"
|
|
|
+ @click="toggleTab('archives')">
|
|
|
+ Archives
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button
|
|
|
+ v-if="isOwner"
|
|
|
+ class="btn btn-link"
|
|
|
+ :class="[ tabIndex === 'bookmarks' ? 'active' : '' ]"
|
|
|
+ @click="toggleTab('bookmarks')">
|
|
|
+ Bookmarks
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button
|
|
|
+ v-if="canViewCollections"
|
|
|
+ class="btn btn-link"
|
|
|
+ :class="[ tabIndex === 2 ? 'active' : '' ]"
|
|
|
+ @click="toggleTab(2)">
|
|
|
+ Collections
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button
|
|
|
+ v-if="isOwner"
|
|
|
+ class="btn btn-link"
|
|
|
+ :class="[ tabIndex === 3 ? 'active' : '' ]"
|
|
|
+ @click="toggleTab(3)">
|
|
|
+ Likes
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="tabIndex === 1" class="btn-group layout-sort-toggle">
|
|
|
+ <button
|
|
|
+ class="btn btn-sm"
|
|
|
+ :class="[ layoutIndex === 0 ? 'btn-dark' : 'btn-light' ]"
|
|
|
+ @click="toggleLayout(0, true)">
|
|
|
+ <i class="far fa-th fa-lg"></i>
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button
|
|
|
+ class="btn btn-sm"
|
|
|
+ :class="[ layoutIndex === 1 ? 'btn-dark' : 'btn-light' ]"
|
|
|
+ @click="toggleLayout(1, true)">
|
|
|
+ <i class="fas fa-th-large fa-lg"></i>
|
|
|
+ </button>
|
|
|
+
|
|
|
+ <button
|
|
|
+ class="btn btn-sm"
|
|
|
+ :class="[ layoutIndex === 2 ? 'btn-dark' : 'btn-light' ]"
|
|
|
+ @click="toggleLayout(2, true)">
|
|
|
+ <i class="far fa-bars fa-lg"></i>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="tabIndex == 0" class="d-flex justify-content-center mt-5">
|
|
|
+ <b-spinner />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="tabIndex == 1" class="px-0 mx-0">
|
|
|
+ <div v-if="layoutIndex === 0" class="row">
|
|
|
+ <div class="col-4 p-1" v-for="(s, index) in feed" :key="'tlob:'+index+s.id">
|
|
|
+ <a v-if="s.hasOwnProperty('pf_type') && s.pf_type == 'video'" class="card info-overlay card-md-border-0" :href="statusUrl(s)">
|
|
|
+ <div class="square">
|
|
|
+ <div v-if="s.sensitive" class="square-content">
|
|
|
+ <div class="info-overlay-text-label">
|
|
|
+ <h5 class="text-white m-auto font-weight-bold">
|
|
|
+ <span>
|
|
|
+ <span class="far fa-eye-slash fa-lg p-2 d-flex-inline"></span>
|
|
|
+ </span>
|
|
|
+ </h5>
|
|
|
+ </div>
|
|
|
+ <blur-hash-canvas
|
|
|
+ width="32"
|
|
|
+ height="32"
|
|
|
+ :hash="s.media_attachments[0].blurhash">
|
|
|
+ </blur-hash-canvas>
|
|
|
+ </div>
|
|
|
+ <div v-else class="square-content">
|
|
|
+ <blur-hash-image
|
|
|
+ width="32"
|
|
|
+ height="32"
|
|
|
+ :hash="s.media_attachments[0].blurhash"
|
|
|
+ :src="s.media_attachments[0].preview_url">
|
|
|
+ </blur-hash-image>
|
|
|
+ </div>
|
|
|
+ <div class="info-overlay-text">
|
|
|
+ <div class="text-white m-auto">
|
|
|
+ <p class="info-overlay-text-field font-weight-bold">
|
|
|
+ <span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
|
|
|
+ <span class="d-flex-inline">{{formatCount(s.favourites_count)}}</span>
|
|
|
+ </p>
|
|
|
+
|
|
|
+ <p class="info-overlay-text-field font-weight-bold">
|
|
|
+ <span class="far fa-comment fa-lg p-2 d-flex-inline"></span>
|
|
|
+ <span class="d-flex-inline">{{formatCount(s.reply_count)}}</span>
|
|
|
+ </p>
|
|
|
+
|
|
|
+ <p class="mb-0 info-overlay-text-field font-weight-bold">
|
|
|
+ <span class="far fa-sync fa-lg p-2 d-flex-inline"></span>
|
|
|
+ <span class="d-flex-inline">{{formatCount(s.reblogs_count)}}</span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <span class="badge badge-light video-overlay-badge">
|
|
|
+ <i class="far fa-video fa-2x"></i>
|
|
|
+ </span>
|
|
|
+
|
|
|
+ <span class="badge badge-light timestamp-overlay-badge">
|
|
|
+ {{ timeago(s.created_at) }}
|
|
|
+ </span>
|
|
|
+ </a>
|
|
|
+ <a v-else class="card info-overlay card-md-border-0" :href="statusUrl(s)">
|
|
|
+ <div class="square">
|
|
|
+ <div v-if="s.sensitive" class="square-content">
|
|
|
+ <div class="info-overlay-text-label">
|
|
|
+ <h5 class="text-white m-auto font-weight-bold">
|
|
|
+ <span>
|
|
|
+ <span class="far fa-eye-slash fa-lg p-2 d-flex-inline"></span>
|
|
|
+ </span>
|
|
|
+ </h5>
|
|
|
+ </div>
|
|
|
+ <blur-hash-canvas
|
|
|
+ width="32"
|
|
|
+ height="32"
|
|
|
+ :hash="s.media_attachments[0].blurhash">
|
|
|
+ </blur-hash-canvas>
|
|
|
+ </div>
|
|
|
+ <div v-else class="square-content">
|
|
|
+ <blur-hash-image
|
|
|
+ width="32"
|
|
|
+ height="32"
|
|
|
+ :hash="s.media_attachments[0].blurhash"
|
|
|
+ :src="s.media_attachments[0].url">
|
|
|
+ </blur-hash-image>
|
|
|
+ </div>
|
|
|
+ <div class="info-overlay-text">
|
|
|
+ <div class="text-white m-auto">
|
|
|
+ <p class="info-overlay-text-field font-weight-bold">
|
|
|
+ <span class="far fa-heart fa-lg p-2 d-flex-inline"></span>
|
|
|
+ <span class="d-flex-inline">{{formatCount(s.favourites_count)}}</span>
|
|
|
+ </p>
|
|
|
+
|
|
|
+ <p class="info-overlay-text-field font-weight-bold">
|
|
|
+ <span class="far fa-comment fa-lg p-2 d-flex-inline"></span>
|
|
|
+ <span class="d-flex-inline">{{formatCount(s.reply_count)}}</span>
|
|
|
+ </p>
|
|
|
+
|
|
|
+ <p class="mb-0 info-overlay-text-field font-weight-bold">
|
|
|
+ <span class="far fa-sync fa-lg p-2 d-flex-inline"></span>
|
|
|
+ <span class="d-flex-inline">{{formatCount(s.reblogs_count)}}</span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <span class="badge badge-light timestamp-overlay-badge">
|
|
|
+ {{ timeago(s.created_at) }}
|
|
|
+ </span>
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <intersect v-if="canLoadMore" @enter="enterIntersect">
|
|
|
+ <div class="col-4 ph-wrapper">
|
|
|
+ <div class="ph-item">
|
|
|
+ <div class="ph-picture big"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </intersect>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="layoutIndex === 1" class="row">
|
|
|
+ <masonry
|
|
|
+ :cols="{default: 3, 800: 2}"
|
|
|
+ :gutter="{default: '5px'}">
|
|
|
+
|
|
|
+ <div class="p-1" v-for="(s, index) in feed" :key="'tlog:'+index+s.id">
|
|
|
+ <a v-if="s.hasOwnProperty('pf_type') && s.pf_type == 'video'" class="card info-overlay card-md-border-0" :href="statusUrl(s)">
|
|
|
+ <div class="square">
|
|
|
+ <div class="square-content">
|
|
|
+ <blur-hash-image
|
|
|
+ width="32"
|
|
|
+ height="32"
|
|
|
+ class="rounded"
|
|
|
+ :hash="s.media_attachments[0].blurhash"
|
|
|
+ :src="s.media_attachments[0].preview_url">
|
|
|
+ </blur-hash-image>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <span class="badge badge-light video-overlay-badge">
|
|
|
+ <i class="far fa-video fa-2x"></i>
|
|
|
+ </span>
|
|
|
+
|
|
|
+ <span class="badge badge-light timestamp-overlay-badge">
|
|
|
+ {{ timeago(s.created_at) }}
|
|
|
+ </span>
|
|
|
+ </a>
|
|
|
+
|
|
|
+ <a v-else-if="s.sensitive" class="card info-overlay card-md-border-0" :href="statusUrl(s)">
|
|
|
+ <div class="square">
|
|
|
+ <div class="square-content">
|
|
|
+ <div class="info-overlay-text-label rounded">
|
|
|
+ <h5 class="text-white m-auto font-weight-bold">
|
|
|
+ <span>
|
|
|
+ <span class="far fa-eye-slash fa-lg p-2 d-flex-inline"></span>
|
|
|
+ </span>
|
|
|
+ </h5>
|
|
|
+ </div>
|
|
|
+ <blur-hash-canvas
|
|
|
+ width="32"
|
|
|
+ height="32"
|
|
|
+ class="rounded"
|
|
|
+ :hash="s.media_attachments[0].blurhash">
|
|
|
+ </blur-hash-canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </a>
|
|
|
+
|
|
|
+ <a v-else class="card info-overlay card-md-border-0" :href="statusUrl(s)">
|
|
|
+ <img :src="previewUrl(s)" class="img-fluid w-100 rounded-lg" onerror="this.onerror=null;this.src='/storage/no-preview.png?v=0'">
|
|
|
+ <span class="badge badge-light timestamp-overlay-badge">
|
|
|
+ {{ timeago(s.created_at) }}
|
|
|
+ </span>
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <intersect v-if="canLoadMore" @enter="enterIntersect">
|
|
|
+ <div class="p-1 ph-wrapper">
|
|
|
+ <div class="ph-item">
|
|
|
+ <div class="ph-picture big"></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </intersect>
|
|
|
+ </masonry>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="layoutIndex === 2" class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-10">
|
|
|
+ <status-card
|
|
|
+ v-for="(s, index) in feed"
|
|
|
+ :key="'prs'+s.id+':'+index"
|
|
|
+ :profile="user"
|
|
|
+ :status="s"
|
|
|
+ v-on:like="likeStatus(index)"
|
|
|
+ v-on:unlike="unlikeStatus(index)"
|
|
|
+ v-on:share="shareStatus(index)"
|
|
|
+ v-on:unshare="unshareStatus(index)"
|
|
|
+ v-on:menu="openContextMenu(index)"
|
|
|
+ v-on:counter-change="counterChange(index, $event)"
|
|
|
+ v-on:likes-modal="openLikesModal(index)"
|
|
|
+ v-on:shares-modal="openSharesModal(index)"
|
|
|
+ v-on:comment-likes-modal="openCommentLikesModal"
|
|
|
+ v-on:handle-report="handleReport" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <intersect v-if="canLoadMore" @enter="enterIntersect">
|
|
|
+ <div class="col-12 col-md-10">
|
|
|
+ <status-placeholder style="margin-bottom: 10rem;" />
|
|
|
+ </div>
|
|
|
+ </intersect>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="feedLoaded && !feed.length">
|
|
|
+ <div class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-8 text-center">
|
|
|
+ <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
+ <p class="lead text-muted font-weight-bold">{{ $t('profile.emptyPosts') }}</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="tabIndex === 'private'" class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-8 text-center">
|
|
|
+ <img src="/img/illustrations/dk-secure-feed.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
+ <p class="h3 text-dark font-weight-bold mt-3 py-3">This profile is private</p>
|
|
|
+ <div class="lead text-muted px-3">
|
|
|
+ Only approved followers can see <span class="font-weight-bold text-dark text-break">@{{ profile.acct }}</span>'s <br />
|
|
|
+ posts. To request access, click <span class="font-weight-bold">Follow</span>.
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="tabIndex == 2" class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-8">
|
|
|
+ <div class="list-group">
|
|
|
+ <a
|
|
|
+ v-for="(collection, index) in collections"
|
|
|
+ class="list-group-item text-decoration-none text-dark"
|
|
|
+ :href="collection.url">
|
|
|
+ <div class="media">
|
|
|
+ <img :src="collection.thumb" width="65" height="65" style="object-fit: cover;" class="rounded-lg border mr-3" onerror="this.onerror=null;this.src='/storage/no-preview.png';">
|
|
|
+ <div class="media-body text-left">
|
|
|
+ <p class="lead mb-0">{{ collection.title ? collection.title : 'Untitled' }}</p>
|
|
|
+ <p class="small text-muted mb-1">{{ collection.description || 'No description available' }}</p>
|
|
|
+ <p class="small text-lighter mb-0 font-weight-bold">
|
|
|
+ <span>{{ collection.post_count }} posts</span>
|
|
|
+ <span>·</span>
|
|
|
+ <span v-if="collection.visibility === 'public'" class="text-dark">Public</span>
|
|
|
+ <span v-else-if="collection.visibility === 'private'" class="text-dark"><i class="far fa-lock fa-sm"></i> Followers Only</span>
|
|
|
+ <span v-else-if="collection.visibility === 'draft'" class="primary"><i class="far fa-lock fa-sm"></i> Draft</span>
|
|
|
+ <span>·</span>
|
|
|
+ <span v-if="collection.published_at">Created {{ timeago(collection.published_at) }} ago</span>
|
|
|
+ <span v-else class="text-warning">UNPUBLISHED</span>
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </a>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="collectionsLoaded && !collections.length" class="col-12 col-md-8 text-center">
|
|
|
+ <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
+ <p class="lead text-muted font-weight-bold">{{ $t('profile.emptyCollections') }}</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="canLoadMoreCollections" class="col-12 col-md-8">
|
|
|
+ <intersect @enter="enterCollectionsIntersect">
|
|
|
+ <div class="d-flex justify-content-center mt-5">
|
|
|
+ <b-spinner small />
|
|
|
+ </div>
|
|
|
+ </intersect>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="tabIndex == 3" class="px-0 mx-0">
|
|
|
+ <div class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-10">
|
|
|
+ <status-card
|
|
|
+ v-for="(s, index) in favourites"
|
|
|
+ :key="'prs'+s.id+':'+index"
|
|
|
+ :profile="user"
|
|
|
+ :status="s"
|
|
|
+ v-on:like="likeStatus(index)"
|
|
|
+ v-on:unlike="unlikeStatus(index)"
|
|
|
+ v-on:share="shareStatus(index)"
|
|
|
+ v-on:unshare="unshareStatus(index)"
|
|
|
+ v-on:counter-change="counterChange(index, $event)"
|
|
|
+ v-on:likes-modal="openLikesModal(index)"
|
|
|
+ v-on:comment-likes-modal="openCommentLikesModal"
|
|
|
+ v-on:handle-report="handleReport" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="canLoadMoreFavourites" class="col-12 col-md-10">
|
|
|
+ <intersect @enter="enterFavouritesIntersect">
|
|
|
+ <status-placeholder style="margin-bottom: 10rem;" />
|
|
|
+ </intersect>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="!favourites || !favourites.length" class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-8 text-center">
|
|
|
+ <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
+ <p class="lead text-muted font-weight-bold">We can't seem to find any posts you have liked</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="tabIndex == 'bookmarks'" class="px-0 mx-0">
|
|
|
+ <div class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-10">
|
|
|
+ <status-card
|
|
|
+ v-for="(s, index) in bookmarks"
|
|
|
+ :key="'prs'+s.id+':'+index"
|
|
|
+ :profile="user"
|
|
|
+ :new-reactions="true"
|
|
|
+ :status="s"
|
|
|
+ v-on:menu="openContextMenu(index)"
|
|
|
+ v-on:counter-change="counterChange(index, $event)"
|
|
|
+ v-on:likes-modal="openLikesModal(index)"
|
|
|
+ v-on:bookmark="handleBookmark(index)"
|
|
|
+ v-on:comment-likes-modal="openCommentLikesModal"
|
|
|
+ v-on:handle-report="handleReport" />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="col-12 col-md-10">
|
|
|
+ <intersect v-if="canLoadMoreBookmarks" @enter="enterBookmarksIntersect">
|
|
|
+ <status-placeholder style="margin-bottom: 10rem;" />
|
|
|
+ </intersect>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="!bookmarks || !bookmarks.length" class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-8 text-center">
|
|
|
+ <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
+ <p class="lead text-muted font-weight-bold">We can't seem to find any posts you have bookmarked</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else-if="tabIndex == 'archives'" class="px-0 mx-0">
|
|
|
+ <div class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-10">
|
|
|
+ <status-card
|
|
|
+ v-for="(s, index) in archives"
|
|
|
+ :key="'prarc'+s.id+':'+index"
|
|
|
+ :profile="user"
|
|
|
+ :new-reactions="true"
|
|
|
+ :reaction-bar="false"
|
|
|
+ :status="s"
|
|
|
+ v-on:menu="openContextMenu(index, 'archive')"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="canLoadMoreArchives" class="col-12 col-md-10">
|
|
|
+ <intersect @enter="enterArchivesIntersect">
|
|
|
+ <status-placeholder style="margin-bottom: 10rem;" />
|
|
|
+ </intersect>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="!archives || !archives.length" class="row justify-content-center">
|
|
|
+ <div class="col-12 col-md-8 text-center">
|
|
|
+ <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;">
|
|
|
+ <p class="lead text-muted font-weight-bold">We can't seem to find any posts you have bookmarked</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <context-menu
|
|
|
+ v-if="showMenu"
|
|
|
+ ref="contextMenu"
|
|
|
+ :status="contextMenuPost"
|
|
|
+ :profile="user"
|
|
|
+ v-on:moderate="commitModeration"
|
|
|
+ v-on:delete="deletePost"
|
|
|
+ v-on:archived="handleArchived"
|
|
|
+ v-on:unarchived="handleUnarchived"
|
|
|
+ v-on:report-modal="handleReport"
|
|
|
+ />
|
|
|
+
|
|
|
+ <likes-modal
|
|
|
+ v-if="showLikesModal"
|
|
|
+ ref="likesModal"
|
|
|
+ :status="likesModalPost"
|
|
|
+ :profile="user"
|
|
|
+ />
|
|
|
+
|
|
|
+ <shares-modal
|
|
|
+ v-if="showSharesModal"
|
|
|
+ ref="sharesModal"
|
|
|
+ :status="sharesModalPost"
|
|
|
+ :profile="profile"
|
|
|
+ />
|
|
|
+
|
|
|
+ <report-modal
|
|
|
+ ref="reportModal"
|
|
|
+ :key="reportedStatusId"
|
|
|
+ :status="reportedStatus"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script type="text/javascript">
|
|
|
+ import Intersect from 'vue-intersect'
|
|
|
+ import StatusCard from './../TimelineStatus.vue';
|
|
|
+ import StatusPlaceholder from './../StatusPlaceholder.vue';
|
|
|
+ import BlurHashCanvas from './../BlurhashCanvas.vue';
|
|
|
+ import ContextMenu from './../../partials/post/ContextMenu.vue';
|
|
|
+ import LikesModal from './../../partials/post/LikeModal.vue';
|
|
|
+ import SharesModal from './../../partials/post/ShareModal.vue';
|
|
|
+ import ReportModal from './../../partials/modal/ReportPost.vue';
|
|
|
+ import { parseLinkHeader } from '@web3-storage/parse-link-header';
|
|
|
+
|
|
|
+ export default {
|
|
|
+ props: {
|
|
|
+ profile: {
|
|
|
+ type: Object
|
|
|
+ },
|
|
|
+
|
|
|
+ relationship: {
|
|
|
+ type: Object
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ components: {
|
|
|
+ "intersect": Intersect,
|
|
|
+ "status-card": StatusCard,
|
|
|
+ "bh-canvas": BlurHashCanvas,
|
|
|
+ "status-placeholder": StatusPlaceholder,
|
|
|
+ "context-menu": ContextMenu,
|
|
|
+ "likes-modal": LikesModal,
|
|
|
+ "shares-modal": SharesModal,
|
|
|
+ "report-modal": ReportModal
|
|
|
+ },
|
|
|
+
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ isLoaded: false,
|
|
|
+ user: {},
|
|
|
+ isOwner: false,
|
|
|
+ layoutIndex: 0,
|
|
|
+ tabIndex: 0,
|
|
|
+ ids: [],
|
|
|
+ feed: [],
|
|
|
+ feedLoaded: false,
|
|
|
+ collections: [],
|
|
|
+ collectionsLoaded: false,
|
|
|
+ canLoadMore: false,
|
|
|
+ max_id: 1,
|
|
|
+ isIntersecting: false,
|
|
|
+ postIndex: 0,
|
|
|
+ showMenu: false,
|
|
|
+ showLikesModal: false,
|
|
|
+ likesModalPost: {},
|
|
|
+ showReportModal: false,
|
|
|
+ reportedStatus: {},
|
|
|
+ reportedStatusId: 0,
|
|
|
+ favourites: [],
|
|
|
+ favouritesLoaded: false,
|
|
|
+ favouritesPage: 1,
|
|
|
+ canLoadMoreFavourites: false,
|
|
|
+ bookmarks: [],
|
|
|
+ bookmarksLoaded: false,
|
|
|
+ bookmarksPage: 1,
|
|
|
+ bookmarksCursor: undefined,
|
|
|
+ canLoadMoreBookmarks: false,
|
|
|
+ canLoadMoreCollections: false,
|
|
|
+ collectionsPage: 1,
|
|
|
+ isCollectionsIntersecting: false,
|
|
|
+ canViewCollections: false,
|
|
|
+ showSharesModal: false,
|
|
|
+ sharesModalPost: {},
|
|
|
+ archives: [],
|
|
|
+ archivesLoaded: false,
|
|
|
+ archivesPage: 1,
|
|
|
+ canLoadMoreArchives: false,
|
|
|
+ contextMenuPost: {}
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ mounted() {
|
|
|
+ this.init();
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ init() {
|
|
|
+ this.user = window._sharedData.user;
|
|
|
+
|
|
|
+ if(this.$store.state.profileLayout != 'grid') {
|
|
|
+ let index = this.$store.state.profileLayout === 'masonry' ? 1 : 2;
|
|
|
+ this.toggleLayout(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(this.user) {
|
|
|
+ this.isOwner = this.user.id == this.profile.id;
|
|
|
+ if(this.isOwner) {
|
|
|
+ this.canViewCollections = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(this.profile.locked) {
|
|
|
+ this.privateProfileCheck();
|
|
|
+ } else {
|
|
|
+ if(this.profile.local) {
|
|
|
+ this.canViewCollections = true;
|
|
|
+ }
|
|
|
+ this.fetchFeed();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ privateProfileCheck() {
|
|
|
+ if(this.relationship.following || this.isOwner) {
|
|
|
+ this.canViewCollections = true;
|
|
|
+ this.fetchFeed();
|
|
|
+ } else {
|
|
|
+ this.tabIndex = 'private';
|
|
|
+ this.isLoaded = true;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ fetchFeed() {
|
|
|
+ axios.get('/api/pixelfed/v1/accounts/' + this.profile.id + '/statuses', {
|
|
|
+ params: {
|
|
|
+ limit: 9,
|
|
|
+ only_media: true,
|
|
|
+ min_id: 1
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.tabIndex = 1;
|
|
|
+ let data = res.data.filter(status => status.media_attachments.length > 0);
|
|
|
+ let ids = data.map(status => status.id);
|
|
|
+ this.ids = ids;
|
|
|
+ this.max_id = Math.min(...ids);
|
|
|
+ data.forEach(s => {
|
|
|
+ this.feed.push(s);
|
|
|
+ });
|
|
|
+ setTimeout(() => {
|
|
|
+ this.canLoadMore = res.data.length > 1;
|
|
|
+ this.feedLoaded = true;
|
|
|
+ }, 500);
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ enterIntersect() {
|
|
|
+ if(this.isIntersecting) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.isIntersecting = true;
|
|
|
+
|
|
|
+ axios.get('/api/pixelfed/v1/accounts/' + this.profile.id + '/statuses', {
|
|
|
+ params: {
|
|
|
+ limit: 9,
|
|
|
+ only_media: true,
|
|
|
+ max_id: this.max_id,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ if(!res.data || !res.data.length) {
|
|
|
+ this.canLoadMore = false;
|
|
|
+ }
|
|
|
+ let data = res.data
|
|
|
+ .filter(status => status.media_attachments.length > 0)
|
|
|
+ .filter(status => this.ids.indexOf(status.id) == -1)
|
|
|
+
|
|
|
+ if(!data || !data.length) {
|
|
|
+ this.canLoadMore = false;
|
|
|
+ this.isIntersecting = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let filtered = data.forEach(status => {
|
|
|
+ if(status.id < this.max_id) {
|
|
|
+ this.max_id = status.id;
|
|
|
+ } else {
|
|
|
+ this.max_id--;
|
|
|
+ }
|
|
|
+ this.ids.push(status.id);
|
|
|
+ this.feed.push(status);
|
|
|
+ });
|
|
|
+ this.isIntersecting = false;
|
|
|
+ this.canLoadMore = res.data.length >= 1;
|
|
|
+ }).catch(err => {
|
|
|
+ this.canLoadMore = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ toggleLayout(idx, blur = false) {
|
|
|
+ if(blur) {
|
|
|
+ event.currentTarget.blur();
|
|
|
+ }
|
|
|
+ this.layoutIndex = idx;
|
|
|
+ this.isIntersecting = false;
|
|
|
+ },
|
|
|
+
|
|
|
+ toggleTab(idx) {
|
|
|
+ event.currentTarget.blur();
|
|
|
+
|
|
|
+ switch(idx) {
|
|
|
+ case 1:
|
|
|
+ this.isIntersecting = false;
|
|
|
+ this.tabIndex = 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 2:
|
|
|
+ this.fetchCollections();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 3:
|
|
|
+ this.fetchFavourites();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'bookmarks':
|
|
|
+ this.fetchBookmarks();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'archives':
|
|
|
+ this.fetchArchives();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ fetchCollections() {
|
|
|
+ if(this.collectionsLoaded) {
|
|
|
+ this.tabIndex = 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ axios.get('/api/local/profile/collections/' + this.profile.id)
|
|
|
+ .then(res => {
|
|
|
+ this.collections = res.data;
|
|
|
+ this.collectionsLoaded = true;
|
|
|
+ this.tabIndex = 2;
|
|
|
+ this.collectionsPage++;
|
|
|
+ this.canLoadMoreCollections = res.data.length === 9;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ enterCollectionsIntersect() {
|
|
|
+ if(this.isCollectionsIntersecting) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.isCollectionsIntersecting = true;
|
|
|
+
|
|
|
+ axios.get('/api/local/profile/collections/' + this.profile.id, {
|
|
|
+ params: {
|
|
|
+ limit: 9,
|
|
|
+ page: this.collectionsPage
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ if(!res.data || !res.data.length) {
|
|
|
+ this.canLoadMoreCollections = false;
|
|
|
+ }
|
|
|
+ this.collectionsLoaded = true;
|
|
|
+ this.collections.push(...res.data);
|
|
|
+ this.collectionsPage++;
|
|
|
+ this.canLoadMoreCollections = res.data.length > 0;
|
|
|
+ this.isCollectionsIntersecting = false;
|
|
|
+ }).catch(err => {
|
|
|
+ this.canLoadMoreCollections = false;
|
|
|
+ this.isCollectionsIntersecting = false;
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ fetchFavourites() {
|
|
|
+ this.tabIndex = 0;
|
|
|
+ axios.get('/api/pixelfed/v1/favourites')
|
|
|
+ .then(res => {
|
|
|
+ this.tabIndex = 3;
|
|
|
+ this.favourites = res.data;
|
|
|
+ this.favouritesPage++;
|
|
|
+ this.favouritesLoaded = true;
|
|
|
+
|
|
|
+ if(res.data.length != 0) {
|
|
|
+ this.canLoadMoreFavourites = true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ enterFavouritesIntersect() {
|
|
|
+ if(this.isIntersecting) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.isIntersecting = true;
|
|
|
+
|
|
|
+ axios.get('/api/pixelfed/v1/favourites', {
|
|
|
+ params: {
|
|
|
+ page: this.favouritesPage,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.favourites.push(...res.data);
|
|
|
+ this.favouritesPage++;
|
|
|
+ this.canLoadMoreFavourites = res.data.length != 0;
|
|
|
+ this.isIntersecting = false;
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ this.canLoadMoreFavourites = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ fetchBookmarks() {
|
|
|
+ this.tabIndex = 0;
|
|
|
+ axios.get('/api/v1/bookmarks', {
|
|
|
+ params: {
|
|
|
+ '_pe': 1
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.tabIndex = 'bookmarks';
|
|
|
+ this.bookmarks = res.data;
|
|
|
+
|
|
|
+ if(res.headers && res.headers.link) {
|
|
|
+ const links = parseLinkHeader(res.headers.link);
|
|
|
+ if(links.next) {
|
|
|
+ this.bookmarksPage = links.next.cursor;
|
|
|
+ this.canLoadMoreBookmarks = true;
|
|
|
+ } else {
|
|
|
+ this.canLoadMoreBookmarks = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.bookmarksLoaded = true;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ enterBookmarksIntersect() {
|
|
|
+ if(this.isIntersecting) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.isIntersecting = true;
|
|
|
+
|
|
|
+ axios.get('/api/v1/bookmarks', {
|
|
|
+ params: {
|
|
|
+ '_pe': 1,
|
|
|
+ cursor: this.bookmarksPage,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.bookmarks.push(...res.data);
|
|
|
+ if(res.headers && res.headers.link) {
|
|
|
+ const links = parseLinkHeader(res.headers.link);
|
|
|
+ if(links.next) {
|
|
|
+ this.bookmarksPage = links.next.cursor;
|
|
|
+ this.canLoadMoreBookmarks = true;
|
|
|
+ } else {
|
|
|
+ this.canLoadMoreBookmarks = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.isIntersecting = false;
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ this.canLoadMoreBookmarks = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ fetchArchives() {
|
|
|
+ this.tabIndex = 0;
|
|
|
+ axios.get('/api/pixelfed/v2/statuses/archives')
|
|
|
+ .then(res => {
|
|
|
+ this.tabIndex = 'archives';
|
|
|
+ this.archives = res.data;
|
|
|
+ this.archivesPage++;
|
|
|
+ this.archivesLoaded = true;
|
|
|
+
|
|
|
+ if(res.data.length != 0) {
|
|
|
+ this.canLoadMoreArchives = true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ formatCount(val) {
|
|
|
+ return App.util.format.count(val);
|
|
|
+ },
|
|
|
+
|
|
|
+ statusUrl(s) {
|
|
|
+ return '/i/web/post/' + s.id;
|
|
|
+ },
|
|
|
+
|
|
|
+ previewUrl(status) {
|
|
|
+ return status.sensitive ? '/storage/no-preview.png?v=' + new Date().getTime() : status.media_attachments[0].url;
|
|
|
+ },
|
|
|
+
|
|
|
+ timeago(ts) {
|
|
|
+ return App.util.format.timeAgo(ts);
|
|
|
+ },
|
|
|
+
|
|
|
+ likeStatus(index) {
|
|
|
+ let status = this.feed[index];
|
|
|
+ let state = status.favourited;
|
|
|
+ let count = status.favourites_count;
|
|
|
+ this.feed[index].favourites_count = count + 1;
|
|
|
+ this.feed[index].favourited = !status.favourited;
|
|
|
+
|
|
|
+ axios.post('/api/v1/statuses/' + status.id + '/favourite')
|
|
|
+ .catch(err => {
|
|
|
+ this.feed[index].favourites_count = count;
|
|
|
+ this.feed[index].favourited = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ unlikeStatus(index) {
|
|
|
+ let status = this.feed[index];
|
|
|
+ let state = status.favourited;
|
|
|
+ let count = status.favourites_count;
|
|
|
+ this.feed[index].favourites_count = count - 1;
|
|
|
+ this.feed[index].favourited = !status.favourited;
|
|
|
+
|
|
|
+ axios.post('/api/v1/statuses/' + status.id + '/unfavourite')
|
|
|
+ .catch(err => {
|
|
|
+ this.feed[index].favourites_count = count;
|
|
|
+ this.feed[index].favourited = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ openContextMenu(idx, type = 'feed') {
|
|
|
+ switch(type) {
|
|
|
+ case 'feed':
|
|
|
+ this.postIndex = idx;
|
|
|
+ this.contextMenuPost = this.feed[idx];
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'archive':
|
|
|
+ this.postIndex = idx;
|
|
|
+ this.contextMenuPost = this.archives[idx];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ this.showMenu = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.contextMenu.open();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ openLikesModal(idx) {
|
|
|
+ this.postIndex = idx;
|
|
|
+ this.likesModalPost = this.feed[this.postIndex];
|
|
|
+ this.showLikesModal = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.likesModal.open();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ openSharesModal(idx) {
|
|
|
+ this.postIndex = idx;
|
|
|
+ this.sharesModalPost = this.feed[this.postIndex];
|
|
|
+ this.showSharesModal = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.sharesModal.open();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ commitModeration(type) {
|
|
|
+ let idx = this.postIndex;
|
|
|
+
|
|
|
+ switch(type) {
|
|
|
+ case 'addcw':
|
|
|
+ this.feed[idx].sensitive = true;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'remcw':
|
|
|
+ this.feed[idx].sensitive = false;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'unlist':
|
|
|
+ this.feed.splice(idx, 1);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'spammer':
|
|
|
+ let id = this.feed[idx].account.id;
|
|
|
+
|
|
|
+ this.feed = this.feed.filter(post => {
|
|
|
+ return post.account.id != id;
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ counterChange(index, type) {
|
|
|
+ switch(type) {
|
|
|
+ case 'comment-increment':
|
|
|
+ this.feed[index].reply_count = this.feed[index].reply_count + 1;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case 'comment-decrement':
|
|
|
+ this.feed[index].reply_count = this.feed[index].reply_count - 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ openCommentLikesModal(post) {
|
|
|
+ this.likesModalPost = post;
|
|
|
+ this.showLikesModal = true;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.$refs.likesModal.open();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ shareStatus(index) {
|
|
|
+ let status = this.feed[index];
|
|
|
+ let state = status.reblogged;
|
|
|
+ let count = status.reblogs_count;
|
|
|
+ this.feed[index].reblogs_count = count + 1;
|
|
|
+ this.feed[index].reblogged = !status.reblogged;
|
|
|
+
|
|
|
+ axios.post('/api/v1/statuses/' + status.id + '/reblog')
|
|
|
+ .catch(err => {
|
|
|
+ this.feed[index].reblogs_count = count;
|
|
|
+ this.feed[index].reblogged = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ unshareStatus(index) {
|
|
|
+ let status = this.feed[index];
|
|
|
+ let state = status.reblogged;
|
|
|
+ let count = status.reblogs_count;
|
|
|
+ this.feed[index].reblogs_count = count - 1;
|
|
|
+ this.feed[index].reblogged = !status.reblogged;
|
|
|
+
|
|
|
+ axios.post('/api/v1/statuses/' + status.id + '/unreblog')
|
|
|
+ .catch(err => {
|
|
|
+ this.feed[index].reblogs_count = count;
|
|
|
+ this.feed[index].reblogged = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleReport(post) {
|
|
|
+ this.reportedStatusId = post.id;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.reportedStatus = post;
|
|
|
+ this.$refs.reportModal.open();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ deletePost() {
|
|
|
+ this.feed.splice(this.postIndex, 1);
|
|
|
+ },
|
|
|
+
|
|
|
+ handleArchived(id) {
|
|
|
+ this.feed.splice(this.postIndex, 1);
|
|
|
+ },
|
|
|
+
|
|
|
+ handleUnarchived(id) {
|
|
|
+ this.feed = [];
|
|
|
+ this.fetchFeed();
|
|
|
+ },
|
|
|
+
|
|
|
+ enterArchivesIntersect() {
|
|
|
+ if(this.isIntersecting) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.isIntersecting = true;
|
|
|
+
|
|
|
+ axios.get('/api/pixelfed/v2/statuses/archives', {
|
|
|
+ params: {
|
|
|
+ page: this.archivesPage
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.archives.push(...res.data);
|
|
|
+ this.archivesPage++;
|
|
|
+ this.canLoadMoreArchives = res.data.length != 0;
|
|
|
+ this.isIntersecting = false;
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ this.canLoadMoreArchives = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ handleBookmark(index) {
|
|
|
+ if(!window.confirm('Are you sure you want to unbookmark this post?')) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ let p = this.bookmarks[index];
|
|
|
+
|
|
|
+ axios.post('/i/bookmark', {
|
|
|
+ item: p.id
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ this.bookmarks = this.bookmarks.map(post => {
|
|
|
+ if(post.id == p.id) {
|
|
|
+ post.bookmarked = false;
|
|
|
+ delete post.bookmarked_at;
|
|
|
+ }
|
|
|
+ return post;
|
|
|
+ });
|
|
|
+ this.bookmarks.splice(index, 1);
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ this.$bvToast.toast('Cannot bookmark post at this time.', {
|
|
|
+ title: 'Bookmark Error',
|
|
|
+ variant: 'danger',
|
|
|
+ autoHideDelay: 5000
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+ .profile-feed-component {
|
|
|
+ margin-top: 0;
|
|
|
+
|
|
|
+ .ph-wrapper {
|
|
|
+ padding: 0.25rem;
|
|
|
+
|
|
|
+ .ph-item {
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ border: none;
|
|
|
+ background-color: transparent;
|
|
|
+
|
|
|
+ .ph-picture {
|
|
|
+ height: auto;
|
|
|
+ padding-bottom: 100%;
|
|
|
+ border-radius: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ & > * {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-overlay-text-field {
|
|
|
+ font-size: 13.5px;
|
|
|
+ margin-bottom: 2px;
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ font-size: 20px;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .video-overlay-badge {
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ right: 10px;
|
|
|
+ opacity: 0.6;
|
|
|
+ color: var(--dark);
|
|
|
+ padding-bottom: 1px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .timestamp-overlay-badge {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 10px;
|
|
|
+ right: 10px;
|
|
|
+ opacity: 0.6;
|
|
|
+ }
|
|
|
+
|
|
|
+ .profile-nav-btns {
|
|
|
+ margin-right: 1rem;
|
|
|
+
|
|
|
+ .btn-group {
|
|
|
+ min-height: 45px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-link {
|
|
|
+ color: var(--text-lighter);
|
|
|
+ font-size: 14px;
|
|
|
+ border-radius: 0;
|
|
|
+ margin-right: 1rem;
|
|
|
+ font-weight: bold;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ color: var(--text-muted);
|
|
|
+ text-decoration: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ color: var(--dark);
|
|
|
+ border-bottom: 1px solid var(--dark);
|
|
|
+ transition: border-bottom 250ms ease-in-out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .layout-sort-toggle {
|
|
|
+ .btn {
|
|
|
+ border: none;
|
|
|
+
|
|
|
+ &.btn-light {
|
|
|
+ opacity: 0.4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|