Refactor Hashtag component from #5427

pull/5437/head
Daniel Supernault 5 months ago
parent 6ea108b67c
commit 0a73094d82
No known key found for this signature in database
GPG Key ID: 23740873EE6F76A1

@ -1,325 +1,336 @@
<template> <template>
<div class="hashtag-component"> <div class="hashtag-component">
<div class="container-fluid mt-3"> <div class="container-fluid mt-3">
<div class="row"> <div class="row">
<div class="col-md-3 d-md-block"> <div class="col-md-3 d-md-block">
<sidebar :user="profile" /> <sidebar :user="profile" />
</div> </div>
<div class="col-md-9"> <div class="col-md-9">
<div class="card border-0 shadow-sm mb-3" style="border-radius: 18px;"> <div class="card border-0 shadow-sm mb-3" style="border-radius: 18px;">
<div class="card-body"> <div class="card-body">
<div class="media align-items-center py-3"> <div class="media align-items-center py-3">
<div class="media-body"> <div class="media-body">
<p class="h3 text-break mb-0"> <p class="h3 text-break mb-0">
<span class="text-lighter">#</span>{{ hashtag.name }} <span class="text-lighter">#</span>{{ hashtag.name }}
</p> </p>
<p v-if="hashtag.count && hashtag.count > 100" class="mb-0 text-muted font-weight-bold"> <p v-if="hashtag.count && hashtag.count > 100" class="mb-0 text-muted font-weight-bold">
{{ formatCount(hashtag.count) }} Posts {{ formatCount(hashtag.count) }} Posts
</p> </p>
</div> </div>
<template v-if="hashtag && hashtag.hasOwnProperty('following') && feed && feed.length"> <template v-if="hashtag && hashtag.hasOwnProperty('following') && feed && feed.length">
<button <button
v-if="hashtag.following" v-if="hashtag.following"
:disabled="followingLoading" :disabled="followingLoading"
class="btn btn-light hashtag-follow border rounded-pill font-weight-bold py-1 px-4" class="btn btn-light hashtag-follow border rounded-pill font-weight-bold py-1 px-4"
@click="unfollowHashtag()" @click="unfollowHashtag()"
> >
<b-spinner v-if="followingLoading" small /> <b-spinner v-if="followingLoading" small />
<span v-else> <span v-else>
{{ $t('profile.unfollow') }} {{ $t('profile.unfollow') }}
</span> </span>
</button> </button>
<button <button
v-else v-else
:disabled="followingLoading" :disabled="followingLoading"
class="btn btn-primary hashtag-follow font-weight-bold rounded-pill py-1 px-4" class="btn btn-primary hashtag-follow font-weight-bold rounded-pill py-1 px-4"
@click="followHashtag()" @click="followHashtag()">
> <b-spinner v-if="followingLoading" small />
<b-spinner v-if="followingLoading" small /> <span v-else>
<span v-else> {{ $t('profile.follow') }}
{{ $t('profile.follow') }} </span>
</span> </button>
</button> </template>
</template> </div>
</div> </div>
</div>
</div>
</div> <template v-if="isLoaded && feedLoaded">
<div class="row mx-0 hashtag-feed">
<template v-if="isLoaded && feedLoaded"> <div class="col-6 col-md-4 col-lg-3 p-1" v-for="(status, index) in feed" :key="'tlob:'+index">
<div class="row mx-0 hashtag-feed"> <a
<div class="col-6 col-md-4 col-lg-3 p-1" v-for="(status, index) in feed" :key="'tlob:'+index"> class="card info-overlay card-md-border-0"
<a :href="statusUrl(status)"
class="card info-overlay card-md-border-0" @click.prevent="goToPost(status)">
:href="statusUrl(status)" <div class="square">
@click.prevent="goToPost(status)"> <div v-if="status.sensitive" class="square-content">
<div class="square"> <div class="info-overlay-text-label">
<div v-if="status.sensitive" class="square-content"> <h5 class="text-white m-auto font-weight-bold">
<div class="info-overlay-text-label"> <span>
<h5 class="text-white m-auto font-weight-bold"> <span class="far fa-eye-slash fa-lg p-2 d-flex-inline"></span>
<span> </span>
<span class="far fa-eye-slash fa-lg p-2 d-flex-inline"></span> </h5>
</span> </div>
</h5> <blur-hash-canvas
</div> width="32"
<blur-hash-canvas height="32"
width="32" :hash="status.media_attachments[0].blurhash"
height="32" />
:hash="status.media_attachments[0].blurhash" </div>
/> <div v-else class="square-content">
</div> <blur-hash-image
<div v-else class="square-content"> width="32"
<blur-hash-image height="32"
width="32" :hash="status.media_attachments[0].blurhash"
height="32" :src="getMediaSource(status)"
:hash="status.media_attachments[0].blurhash" />
:src="status.media_attachments[0].preview_url" </div>
/> <span v-if="status.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span>
</div> <span v-if="status.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span>
<span v-if="status.pf_type == 'photo:album'" class="float-right mr-3 post-icon"><i class="fas fa-images fa-2x"></i></span> <span v-if="status.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span>
<span v-if="status.pf_type == 'video'" class="float-right mr-3 post-icon"><i class="fas fa-video fa-2x"></i></span> <div class="info-overlay-text">
<span v-if="status.pf_type == 'video:album'" class="float-right mr-3 post-icon"><i class="fas fa-film fa-2x"></i></span> <h5 class="text-white m-auto font-weight-bold">
<div class="info-overlay-text"> <span>
<h5 class="text-white m-auto font-weight-bold"> <span class="far fa-comment fa-lg p-2 d-flex-inline"></span>
<span> <span class="d-flex-inline">{{formatCount(status.reply_count)}}</span>
<span class="far fa-comment fa-lg p-2 d-flex-inline"></span> </span>
<span class="d-flex-inline">{{formatCount(status.reply_count)}}</span> </h5>
</span> </div>
</h5> </div>
</div> </a>
</div> </div>
</a>
</div> <div v-if="canLoadMore" class="col-12">
<intersect @enter="enterIntersect">
<div v-if="canLoadMore" class="col-12"> <div class="d-flex justify-content-center py-5">
<intersect @enter="enterIntersect"> <b-spinner />
<div class="d-flex justify-content-center py-5"> </div>
<b-spinner /> </intersect>
</div>
</intersect>
</div>
<!-- <div v-else class="ph-item"> </div>
<div class="ph-picture big"></div>
</div> --> <div v-if="feedLoaded && !feed.length" class="row mx-0 hashtag-feed justify-content-center">
</div> <div class="col-12 col-md-8 text-center">
</div> <img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;max-width:400px">
<p class="lead text-muted font-weight-bold">{{ $t('hashtags.emptyFeed') }}</p>
<div v-if="feedLoaded && !feed.length" class="row mx-0 hashtag-feed justify-content-center"> </div>
<div class="col-12 col-md-8 text-center"> </div>
<img src="/img/illustrations/dk-nature-man-monochrome.svg" class="img-fluid" style="opacity: 0.6;max-width:400px"> </template>
<p class="lead text-muted font-weight-bold">{{ $t('hashtags.emptyFeed') }}</p>
</div> <template v-else>
</div> <div class="row justify-content-center align-items-center pt-5 mt-5">
</template> <b-spinner />
</div>
<template v-else> </template>
<div class="row justify-content-center align-items-center pt-5 mt-5"> </div>
<b-spinner /> </div>
</div>
</template> <drawer />
</div> </div>
</div> </div>
<drawer />
</div>
</div>
</template> </template>
<script type="text/javascript"> <script type="text/javascript">
import Drawer from './partials/drawer.vue'; import Drawer from './partials/drawer.vue';
import Intersect from 'vue-intersect' import Intersect from 'vue-intersect'
import Sidebar from './partials/sidebar.vue'; import Sidebar from './partials/sidebar.vue';
import Rightbar from './partials/rightbar.vue'; import Rightbar from './partials/rightbar.vue';
export default { export default {
props: { props: {
id: { id: {
type: String type: String
} }
}, },
components: { components: {
"drawer": Drawer, "drawer": Drawer,
"intersect": Intersect, "intersect": Intersect,
"sidebar": Sidebar, "sidebar": Sidebar,
"rightbar": Rightbar, "rightbar": Rightbar,
}, },
data() { data() {
return { return {
isLoaded: false, isLoaded: false,
profile: undefined, profile: undefined,
canLoadMore: false, canLoadMore: false,
isIntersecting: false, isIntersecting: false,
feedLoaded: false, feedLoaded: false,
feed: [], feed: [],
page: 1, page: 1,
hashtag: { hashtag: {
name: this.id, name: this.id,
count: 0 count: 0
}, },
followingLoading: false, followingLoading: false,
maxId: undefined, maxId: undefined,
}; };
},
mounted() {
this.init();
}, },
mounted() { watch: {
this.init(); '$route': 'init'
}, },
watch: { methods: {
'$route': 'init' init() {
}, this.profile = window._sharedData.user;
axios.get('/api/v1/tags/' + this.id, {
methods: { params: {
init() { '_pe': 1
this.profile = window._sharedData.user; }
axios.get('/api/v1/tags/' + this.id, { })
params: { .then(res => {
'_pe': 1 this.hashtag = res.data;
} })
}) .catch(err => {
.then(res => { swal('Error', 'Something went wrong, please try again later!', 'error');
this.hashtag = res.data; this.isLoaded = true;
}) this.feedLoaded = true;
.catch(err => { })
swal('Error', 'Something went wrong, please try again later!', 'error'); .finally(() => {
this.isLoaded = true; this.fetchFeed();
this.feedLoaded = true; })
}) },
.finally(() => {
this.fetchFeed(); fetchFeed() {
}) axios.get('/api/v1/timelines/tag/' + this.id, {
}, params: {
limit: 80,
fetchFeed() { }
axios.get('/api/v1/timelines/tag/' + this.id, { })
params: { .then(res => {
limit: 80, if(res.data && res.data.length) {
} this.feed = res.data;
}) this.maxId = res.data[res.data.length - 1].id;
.then(res => {
if(res.data && res.data.length) {
this.feed = res.data;
this.maxId = res.data[res.data.length - 1].id;
this.canLoadMore = true;
} else {
this.feedLoaded = true;
this.isLoaded = true;
}
})
.finally(() => {
this.feedLoaded = true;
this.isLoaded = true;
})
},
statusUrl(status) {
return '/i/web/post/' + status.id;
},
formatCount(val) {
return App.util.format.count(val);
},
enterIntersect() {
if(this.isIntersecting) {
return;
}
this.isIntersecting = true;
axios.get('/api/v1/timelines/tag/' + this.id, {
params: {
max_id: this.maxId,
limit: 40,
}
})
.then(res => {
if(res.data && res.data.length) {
this.feed.push(...res.data);
this.maxId = res.data[res.data.length - 1].id;
this.canLoadMore = true; this.canLoadMore = true;
} else { } else {
this.feedLoaded = true;
this.isLoaded = true;
}
})
.finally(() => {
this.feedLoaded = true;
this.isLoaded = true;
})
},
statusUrl(status) {
return '/i/web/post/' + status.id;
},
formatCount(val) {
return App.util.format.count(val);
},
enterIntersect() {
if(this.isIntersecting) {
return;
}
this.isIntersecting = true;
axios.get('/api/v1/timelines/tag/' + this.id, {
params: {
max_id: this.maxId,
limit: 40,
}
})
.then(res => {
if(res.data && res.data.length) {
this.feed.push(...res.data);
this.maxId = res.data[res.data.length - 1].id;
this.canLoadMore = true;
} else {
this.canLoadMore = false; this.canLoadMore = false;
} }
}) })
.finally(() => { .finally(() => {
this.isIntersecting = false; this.isIntersecting = false;
}) })
}, },
goToPost(status) { goToPost(status) {
this.$router.push({ this.$router.push({
name: 'post', name: 'post',
path: `/i/web/post/${status.id}`, path: `/i/web/post/${status.id}`,
params: { params: {
id: status.id, id: status.id,
cachedStatus: status, cachedStatus: status,
cachedProfile: this.profile cachedProfile: this.profile
} }
}) })
}, },
followHashtag() { followHashtag() {
this.followingLoading = true; this.followingLoading = true;
axios.post('/api/v1/tags/' + this.id + '/follow') axios.post('/api/v1/tags/' + this.id + '/follow')
.then(res => { .then(res => {
setTimeout(() => { setTimeout(() => {
this.hashtag.following = true; this.hashtag.following = true;
this.followingLoading = false; this.followingLoading = false;
}, 500); }, 500);
}); });
}, },
unfollowHashtag() { unfollowHashtag() {
this.followingLoading = true; this.followingLoading = true;
axios.post('/api/v1/tags/' + this.id + '/unfollow') axios.post('/api/v1/tags/' + this.id + '/unfollow')
.then(res => { .then(res => {
setTimeout(() => { setTimeout(() => {
this.hashtag.following = false; this.hashtag.following = false;
this.followingLoading = false; this.followingLoading = false;
}, 500); }, 500);
}); });
}, },
}
} getMediaSource(status) {
let media = status.media_attachments[0];
if(media.preview_url && media.preview_url.endsWith('storage/no-preview.png')) {
return media.url;
}
if(media.preview_url && media.preview_url.length) {
return media.url;
}
return media.url;
}
}
}
</script> </script>
<style lang="scss"> <style lang="scss">
.hashtag-component { .hashtag-component {
.hashtag-feed { .hashtag-feed {
.card, .card,
.info-overlay-text, .info-overlay-text,
.info-overlay-text-label, .info-overlay-text-label,
img, img,
canvas { canvas {
border-radius: 18px !important; border-radius: 18px !important;
} }
} }
.hashtag-follow { .hashtag-follow {
width: 200px; width: 200px;
} }
.ph-wrapper { .ph-wrapper {
padding: 0.25rem; padding: 0.25rem;
.ph-item { .ph-item {
margin: 0; margin: 0;
padding: 0; padding: 0;
border: none; border: none;
background-color: transparent; background-color: transparent;
.ph-picture { .ph-picture {
height: auto; height: auto;
padding-bottom: 100%; padding-bottom: 100%;
border-radius: 18px; border-radius: 18px;
} }
& > * { & > * {
margin-bottom: 0; margin-bottom: 0;
} }
} }
} }
} }
</style> </style>

Loading…
Cancel
Save