mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-22 13:03:16 +00:00
Add Discover components
This commit is contained in:
parent
bb97b55c66
commit
b447db082f
7 changed files with 1762 additions and 0 deletions
405
resources/assets/components/Discover.vue
Normal file
405
resources/assets/components/Discover.vue
Normal file
|
@ -0,0 +1,405 @@
|
|||
<template>
|
||||
<div class="web-wrapper">
|
||||
<div v-if="isLoaded" class="container-fluid mt-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<sidebar :user="profile" />
|
||||
</div>
|
||||
|
||||
<div v-if="tab == 'index'" class="col-md-8 col-lg-9 mt-n4">
|
||||
<div v-if="profile.is_admin" class="d-md-flex my-md-3">
|
||||
<grid-card
|
||||
:dark="true"
|
||||
:title="'Hello ' + profile.username"
|
||||
subtitle="Welcome to the new Discover experience! Only admins can see this"
|
||||
button-text="Manage Discover Settings"
|
||||
button-link="/i/web/discover/settings"
|
||||
icon-class="fal fa-cog"
|
||||
:small="true" />
|
||||
</div>
|
||||
<!-- <section class="mb-1 mb-md-3 mb-lg-4">
|
||||
<news-slider />
|
||||
</section> -->
|
||||
|
||||
<!-- <discover-spotlight /> -->
|
||||
|
||||
<!-- <div class="d-md-flex my-md-3">
|
||||
<grid-card
|
||||
:dark="true"
|
||||
title="The Not So Trending"
|
||||
subtitle="Explore the posts that deserve more attention"
|
||||
button-text="Explore posts"
|
||||
icon-class="fal fa-analytics"
|
||||
button-link="/i/web/discover/future-trending"
|
||||
:button-event="true"
|
||||
v-on:btn-click="toggleTab('trending')"
|
||||
:small="true" />
|
||||
|
||||
<grid-card
|
||||
title="Behind The Posts"
|
||||
subtitle="Discover the people"
|
||||
button-text="Discover People"
|
||||
button-link="/i/web/discover/people"
|
||||
icon-class="fal fa-user-friends"
|
||||
:small="true" />
|
||||
</div> -->
|
||||
|
||||
<daily-trending v-on:btn-click="toggleTab('trending')"/>
|
||||
|
||||
<!-- <div class="d-md-flex my-md-3">
|
||||
<grid-card
|
||||
title="Explore Loops"
|
||||
subtitle="Loops are short, looping videos"
|
||||
button-text="Explore Loops"
|
||||
icon-class="fal fa-camcorder"
|
||||
button-link="/i/web/discover/loops"
|
||||
:small="false" />
|
||||
|
||||
<grid-card
|
||||
:dark="true"
|
||||
title="Popular Places"
|
||||
subtitle="Explore posts by popular locations"
|
||||
button-text="Explore Popular Places"
|
||||
icon-class="fal fa-map"
|
||||
:button-event="true"
|
||||
v-on:btn-click="toggleTab('popular-places')"
|
||||
button-link="/i/web/discover/popular-places"
|
||||
:small="false" />
|
||||
</div> -->
|
||||
|
||||
<div class="d-md-flex my-md-3">
|
||||
<grid-card
|
||||
v-if="config.hashtags.enabled"
|
||||
:dark="true"
|
||||
title="My Hashtags"
|
||||
subtitle="Explore posts tagged with hashtags you follow"
|
||||
button-text="Explore Posts"
|
||||
button-link="/i/web/discover/my-hashtags"
|
||||
icon-class="fal fa-hashtag"
|
||||
:small="false" />
|
||||
|
||||
<grid-card
|
||||
v-if="config.memories.enabled"
|
||||
title="My Memories"
|
||||
subtitle="A distant look back"
|
||||
button-text="View Memories"
|
||||
button-link="/i/web/discover/my-memories"
|
||||
icon-class="fal fa-history"
|
||||
:small="false" />
|
||||
</div>
|
||||
|
||||
<div class="d-md-flex my-md-3">
|
||||
<grid-card
|
||||
v-if="config.insights.enabled"
|
||||
title="Account Insights"
|
||||
subtitle="Get a rich overview of your account activity and interactions"
|
||||
button-text="View Account Insights"
|
||||
icon-class="fal fa-user-circle"
|
||||
button-link="/i/web/discover/account-insights"
|
||||
:small="false" />
|
||||
|
||||
<grid-card
|
||||
v-if="config.friends.enabled"
|
||||
:dark="true"
|
||||
title="Find Friends"
|
||||
subtitle="Find accounts to follow based on common interests"
|
||||
button-text="Find Friends & Followers"
|
||||
button-link="/i/web/discover/find-friends"
|
||||
icon-class="fal fa-user-plus"
|
||||
:small="false" />
|
||||
</div>
|
||||
|
||||
<div class="d-md-flex my-md-3">
|
||||
<grid-card
|
||||
v-if="config.server.enabled && config.server.domains && config.server.domains.length"
|
||||
:dark="true"
|
||||
title="Server Timelines"
|
||||
subtitle="Browse timelines of a specific remote instance"
|
||||
button-text="Browse Server Feeds"
|
||||
icon-class="fal fa-list"
|
||||
button-link="/i/web/discover/server-timelines"
|
||||
:small="false" />
|
||||
|
||||
<!-- <grid-card
|
||||
title="Curate the Spotlight"
|
||||
subtitle="Apply to curate the spotlight for one week"
|
||||
button-text="Apply to Curate Spotlight"
|
||||
button-link="/i/web/discover/spotlight/curate/apply"
|
||||
icon-class="fal fa-thumbs-up"
|
||||
:small="false" /> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="tab == 'trending'" class="col-md-8 col-lg-9 mt-n4">
|
||||
<discover :profile="profile" />
|
||||
</div>
|
||||
|
||||
<div v-else-if="tab == 'popular-places'" class="col-md-8 col-lg-9 mt-n4">
|
||||
<section class="mt-3 mb-5 section-explore">
|
||||
<div class="profile-timeline">
|
||||
<div class="row p-0 mt-5">
|
||||
<div class="col-12 mb-4 d-flex justify-content-between align-items-center">
|
||||
<p class="d-block d-md-none h1 font-weight-bold mb-0 font-default">Popular Places</p>
|
||||
<p class="d-none d-md-block display-4 font-weight-bold mb-0 font-default">Popular Places</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-5">
|
||||
<div class="col-12 col-md-12 mb-3">
|
||||
<div class="card-img big">
|
||||
<img src="/img/places/nyc.jpg">
|
||||
<div class="title font-default">New York City</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<div class="card-img">
|
||||
<img src="/img/places/edmonton.jpg">
|
||||
<div class="title font-default">Edmonton</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<div class="card-img">
|
||||
<img src="/img/places/paris.jpg">
|
||||
<div class="title font-default">Paris</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4 mb-3">
|
||||
<div class="card-img">
|
||||
<img src="/img/places/london.jpg">
|
||||
<div class="title font-default">London</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4 mb-3">
|
||||
<div class="card-img">
|
||||
<img src="/img/places/vancouver.jpg">
|
||||
<div class="title font-default">Vancouver</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-4 mb-3">
|
||||
<div class="card-img">
|
||||
<img src="/img/places/toronto.jpg">
|
||||
<div class="title font-default">Toronto</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<drawer />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Drawer from './partials/drawer.vue';
|
||||
import Sidebar from './partials/sidebar.vue';
|
||||
import Rightbar from './partials/rightbar.vue';
|
||||
import Discover from './sections/DiscoverFeed.vue';
|
||||
import DiscoverNewsSlider from './partials/discover/news-slider.vue';
|
||||
import DiscoverSpotlight from './partials/discover/discover-spotlight.vue';
|
||||
import DailyTrending from './partials/discover/daily-trending.vue';
|
||||
import DiscoverGridCard from './partials/discover/grid-card.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"rightbar": Rightbar,
|
||||
"discover": Discover,
|
||||
"news-slider": DiscoverNewsSlider,
|
||||
"discover-spotlight": DiscoverSpotlight,
|
||||
"daily-trending": DailyTrending,
|
||||
"grid-card": DiscoverGridCard
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: false,
|
||||
profile: undefined,
|
||||
config: {},
|
||||
tab: 'index',
|
||||
popularAccounts: [],
|
||||
followingIndex: undefined
|
||||
}
|
||||
},
|
||||
|
||||
updated() {
|
||||
// let u = new URLSearchParams(window.location.search);
|
||||
// if(u.has('ft') && u.get('ft') == '1') {
|
||||
// this.tab = 'index';
|
||||
// }
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.profile = window._sharedData.user;
|
||||
this.fetchConfig();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
axios.get('/api/pixelfed/v2/discover/meta')
|
||||
.then(res => {
|
||||
this.config = res.data;
|
||||
this.isLoaded = true;
|
||||
window._sharedData.discoverMeta = res.data;
|
||||
// this.fetchPopularAccounts();
|
||||
})
|
||||
},
|
||||
|
||||
fetchPopularAccounts() {
|
||||
// axios.get('/api/pixelfed/discover/accounts/popular')
|
||||
// .then(res => {
|
||||
// this.popularAccounts = res.data;
|
||||
// })
|
||||
},
|
||||
|
||||
followProfile(index) {
|
||||
event.currentTarget.blur();
|
||||
this.followingIndex = index;
|
||||
let id = this.popularAccounts[index].id;
|
||||
|
||||
axios.post('/api/v1/accounts/' + id + '/follow')
|
||||
.then(res => {
|
||||
this.followingIndex = undefined;
|
||||
this.popularAccounts.splice(index, 1);
|
||||
}).catch(err => {
|
||||
this.followingIndex = undefined;
|
||||
swal('Oops!', 'An error occured when attempting to follow this account.', 'error');
|
||||
});
|
||||
},
|
||||
|
||||
goToProfile(account) {
|
||||
this.$router.push({
|
||||
path: `/i/web/profile/${account.id}`,
|
||||
params: {
|
||||
id: account.id,
|
||||
cachedProfile: account,
|
||||
cachedUser: this.profile
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
toggleTab(index) {
|
||||
this.tab = index;
|
||||
setTimeout(() => {
|
||||
window.scrollTo({top: 0, behavior: 'smooth'});
|
||||
}, 300);
|
||||
},
|
||||
|
||||
openManageModal() {
|
||||
event.currentTarget.blur();
|
||||
swal('Settings', 'Discover settings here', 'info');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.card-img {
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-radius: 10px;
|
||||
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
background: rgba(0,0,0,0.2);
|
||||
z-index: 2;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 10px;
|
||||
font-size: 40px;
|
||||
color: #fff;
|
||||
z-index: 3;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&.big {
|
||||
img {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.font-default {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.bg-stellar {
|
||||
background: #7474BF;
|
||||
background: -webkit-linear-gradient(to right, #348AC7, #7474BF);
|
||||
background: linear-gradient(to right, #348AC7, #7474BF);
|
||||
}
|
||||
|
||||
.bg-berry {
|
||||
background: #5433FF;
|
||||
background: -webkit-linear-gradient(to right, #acb6e5, #86fde8);
|
||||
background: linear-gradient(to right, #acb6e5, #86fde8);
|
||||
}
|
||||
|
||||
.bg-midnight {
|
||||
background: #232526;
|
||||
background: -webkit-linear-gradient(to right, #414345, #232526);
|
||||
background: linear-gradient(to right, #414345, #232526);
|
||||
}
|
||||
|
||||
.media-body {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
margin-bottom: 2px;
|
||||
word-break: break-word !important;
|
||||
word-wrap: break-word !important;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
margin-bottom: 0;
|
||||
font-size: 12px;
|
||||
word-break: break-word !important;
|
||||
word-wrap: break-word !important;
|
||||
}
|
||||
|
||||
.follower-count {
|
||||
margin-bottom: 0;
|
||||
font-size: 10px;
|
||||
word-break: break-word !important;
|
||||
word-wrap: break-word !important;
|
||||
}
|
||||
|
||||
.follow {
|
||||
background-color: var(--primary);
|
||||
border-radius: 18px;
|
||||
font-weight: 600;
|
||||
padding: 5px 15px;
|
||||
}
|
||||
</style>
|
182
resources/assets/components/discover/FindFriends.vue
Normal file
182
resources/assets/components/discover/FindFriends.vue
Normal file
|
@ -0,0 +1,182 @@
|
|||
<template>
|
||||
<div class="discover-find-friends-component">
|
||||
<div v-if="isLoaded" class="container-fluid mt-3">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<sidebar :user="profile" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-6">
|
||||
<b-breadcrumb class="font-default" :items="breadcrumbItems"></b-breadcrumb>
|
||||
|
||||
<h1 class="font-default">Find Friends</h1>
|
||||
<!-- <p class="font-default lead">Posts from hashtags you follow</p> -->
|
||||
<hr>
|
||||
|
||||
<b-spinner v-if="isLoading" />
|
||||
|
||||
<div v-if="!isLoading" class="row justify-content-center">
|
||||
<div class="col-12 col-lg-10 mb-3" v-for="(profile, index) in popularAccounts">
|
||||
<div class="card shadow-sm border-0 rounded-px">
|
||||
<div class="card-body p-2">
|
||||
<profile-card
|
||||
:key="'pfc' + index"
|
||||
:profile="profile"
|
||||
class="w-100"
|
||||
v-on:follow="follow(index)"
|
||||
v-on:unfollow="unfollow(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Drawer from './../partials/drawer.vue';
|
||||
import Sidebar from './../partials/sidebar.vue';
|
||||
import StatusCard from './../partials/TimelineStatus.vue';
|
||||
import ProfileCard from './../partials/profile/ProfileHoverCard.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"status-card": StatusCard,
|
||||
"profile-card": ProfileCard
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: true,
|
||||
isLoading: true,
|
||||
profile: window._sharedData.user,
|
||||
feed: [],
|
||||
popular: [],
|
||||
popularAccounts: [],
|
||||
popularLoaded: false,
|
||||
breadcrumbItems: [
|
||||
{
|
||||
text: 'Discover',
|
||||
href: '/i/web/discover'
|
||||
},
|
||||
{
|
||||
text: 'Find Friends',
|
||||
active: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchConfig();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
axios.get('/api/pixelfed/v2/discover/meta')
|
||||
.then(res => {
|
||||
if(res.data.friends.enabled == false) {
|
||||
this.$router.push('/i/web/discover');
|
||||
} else {
|
||||
this.fetchPopularAccounts();
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
this.isLoading = false;
|
||||
})
|
||||
},
|
||||
|
||||
fetchPopular() {
|
||||
axios.get('/api/pixelfed/v2/discover/account-insights')
|
||||
.then(res => {
|
||||
this.popular = res.data;
|
||||
this.popularLoaded = true;
|
||||
this.isLoading = false;
|
||||
})
|
||||
.catch(e => {
|
||||
this.isLoading = false;
|
||||
})
|
||||
},
|
||||
|
||||
formatCount(val) {
|
||||
return App.util.format.count(val);
|
||||
},
|
||||
|
||||
timeago(ts) {
|
||||
return App.util.format.timeAgo(ts);
|
||||
},
|
||||
|
||||
fetchPopularAccounts() {
|
||||
axios.get('/api/pixelfed/discover/accounts/popular')
|
||||
.then(res => {
|
||||
this.popularAccounts = res.data;
|
||||
this.isLoading = false;
|
||||
})
|
||||
.catch(e => {
|
||||
this.isLoading = false;
|
||||
})
|
||||
},
|
||||
|
||||
follow(index) {
|
||||
axios.post('/api/v1/accounts/' + this.popularAccounts[index].id + '/follow')
|
||||
.then(res => {
|
||||
this.newlyFollowed++;
|
||||
this.$store.commit('updateRelationship', [res.data]);
|
||||
this.$emit('update-profile', {
|
||||
'following_count': this.profile.following_count + 1
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
unfollow(index) {
|
||||
axios.post('/api/v1/accounts/' + this.popularAccounts[index].id + '/unfollow')
|
||||
.then(res => {
|
||||
this.newlyFollowed--;
|
||||
this.$store.commit('updateRelationship', [res.data]);
|
||||
this.$emit('update-profile', {
|
||||
'following_count': this.profile.following_count - 1
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.discover-find-friends-component {
|
||||
.bg-stellar {
|
||||
background: #7474BF;
|
||||
background: -webkit-linear-gradient(to right, #348AC7, #7474BF);
|
||||
background: linear-gradient(to right, #348AC7, #7474BF);
|
||||
}
|
||||
|
||||
.bg-midnight {
|
||||
background: #232526;
|
||||
background: -webkit-linear-gradient(to right, #414345, #232526);
|
||||
background: linear-gradient(to right, #414345, #232526);
|
||||
}
|
||||
|
||||
.font-default {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.profile-hover-card-inner {
|
||||
width: 100%;
|
||||
|
||||
.d-flex {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
384
resources/assets/components/discover/Hashtags.vue
Normal file
384
resources/assets/components/discover/Hashtags.vue
Normal file
|
@ -0,0 +1,384 @@
|
|||
<template>
|
||||
<div class="discover-my-hashtags-component">
|
||||
<div v-if="isLoaded" class="container-fluid mt-3">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<sidebar :user="profile" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-6">
|
||||
<b-breadcrumb class="font-default" :items="breadcrumbItems"></b-breadcrumb>
|
||||
|
||||
<h1 class="font-default">My Hashtags</h1>
|
||||
<p class="font-default lead">Posts from hashtags you follow</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<b-spinner v-if="isLoading" />
|
||||
|
||||
<status-card
|
||||
v-if="!isLoading"
|
||||
v-for="(post, index) in feed"
|
||||
:key="'ti1:'+index+':'+post.id"
|
||||
:profile="profile"
|
||||
:status="post"
|
||||
@like="likeStatus(index)"
|
||||
@unlike="unlikeStatus(index)"
|
||||
@share="shareStatus(index)"
|
||||
@unshare="unshareStatus(index)"
|
||||
@menu="openContextMenu(index)"
|
||||
@mod-tools="handleModTools(index)"
|
||||
@likes-modal="openLikesModal(index)"
|
||||
@shares-modal="openSharesModal(index)"
|
||||
@bookmark="handleBookmark(index)"
|
||||
/>
|
||||
|
||||
<p v-if="!isLoading && tagsLoaded && feed.length == 0" class="lead">No hashtags found :(</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 col-lg-3">
|
||||
<div class="nav flex-column nav-pills font-default">
|
||||
<a
|
||||
v-for="(tag, idx) in tags"
|
||||
class="nav-link"
|
||||
:class="{ active: tagIndex == idx }"
|
||||
href="#"
|
||||
@click.prevent="toggleTag(idx)">
|
||||
{{ tag }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<context-menu
|
||||
v-if="showMenu"
|
||||
ref="contextMenu"
|
||||
:status="feed[postIndex]"
|
||||
:profile="profile"
|
||||
@moderate="commitModeration"
|
||||
@delete="deletePost"
|
||||
@report-modal="handleReport"
|
||||
/>
|
||||
|
||||
<likes-modal
|
||||
v-if="showLikesModal"
|
||||
ref="likesModal"
|
||||
:status="likesModalPost"
|
||||
:profile="profile"
|
||||
/>
|
||||
|
||||
<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 Drawer from './../partials/drawer.vue';
|
||||
import Sidebar from './../partials/sidebar.vue';
|
||||
import StatusCard from './../partials/TimelineStatus.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';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"context-menu": ContextMenu,
|
||||
"likes-modal": LikesModal,
|
||||
"shares-modal": SharesModal,
|
||||
"report-modal": ReportModal,
|
||||
"status-card": StatusCard
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: true,
|
||||
isLoading: true,
|
||||
profile: window._sharedData.user,
|
||||
tagIndex: 0,
|
||||
tags: [],
|
||||
feed: [],
|
||||
tagsLoaded: false,
|
||||
breadcrumbItems: [
|
||||
{
|
||||
text: 'Discover',
|
||||
href: '/i/web/discover'
|
||||
},
|
||||
{
|
||||
text: 'My Hashtags',
|
||||
active: true
|
||||
}
|
||||
],
|
||||
canLoadMore: true,
|
||||
isFetchingMore: false,
|
||||
endFeedReached: false,
|
||||
postIndex: 0,
|
||||
showMenu: false,
|
||||
showLikesModal: false,
|
||||
likesModalPost: {},
|
||||
showReportModal: false,
|
||||
reportedStatus: {},
|
||||
reportedStatusId: 0,
|
||||
showSharesModal: false,
|
||||
sharesModalPost: {},
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchHashtags();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchHashtags() {
|
||||
axios.get('/api/local/discover/tag/list')
|
||||
.then(res => {
|
||||
this.tags = res.data;
|
||||
this.tagsLoaded = true;
|
||||
if(this.tags.length) {
|
||||
this.fetchTagFeed(this.tags[0]);
|
||||
} else {
|
||||
this.isLoading = false;
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
this.isLoading = false;
|
||||
})
|
||||
},
|
||||
|
||||
fetchTagFeed(hashtag) {
|
||||
this.isLoading = true;
|
||||
axios.get('/api/v2/discover/tag', {
|
||||
params: {
|
||||
hashtag: hashtag
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
this.feed = res.data.tags.map(p => p.status);
|
||||
this.isLoading = false;
|
||||
})
|
||||
.catch(e => {
|
||||
this.isLoading = false;
|
||||
})
|
||||
},
|
||||
|
||||
toggleTag(tag) {
|
||||
this.tagIndex = tag;
|
||||
this.fetchTagFeed(this.tags[tag]);
|
||||
},
|
||||
|
||||
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')
|
||||
.then(res => {
|
||||
//
|
||||
}).catch(err => {
|
||||
this.feed[index].favourites_count = count;
|
||||
this.feed[index].favourited = false;
|
||||
|
||||
let el = document.createElement('p');
|
||||
el.classList.add('text-left');
|
||||
el.classList.add('mb-0');
|
||||
el.innerHTML = '<span class="lead">We limit certain interactions to keep our community healthy and it appears that you have reached that limit. <span class="font-weight-bold">Please try again later.</span></span>';
|
||||
let wrapper = document.createElement('div');
|
||||
wrapper.appendChild(el);
|
||||
|
||||
if(err.response.status === 429) {
|
||||
swal({
|
||||
title: 'Too many requests',
|
||||
content: wrapper,
|
||||
icon: 'warning',
|
||||
buttons: {
|
||||
// moreInfo: {
|
||||
// text: "Contact a human",
|
||||
// visible: true,
|
||||
// value: "more",
|
||||
// className: "text-lighter bg-transparent border"
|
||||
// },
|
||||
confirm: {
|
||||
text: "OK",
|
||||
value: false,
|
||||
visible: true,
|
||||
className: "bg-transparent primary",
|
||||
closeModal: true
|
||||
}
|
||||
}
|
||||
})
|
||||
.then((val) => {
|
||||
if(val == 'more') {
|
||||
location.href = '/site/contact'
|
||||
}
|
||||
return;
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
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')
|
||||
.then(res => {
|
||||
//
|
||||
}).catch(err => {
|
||||
this.feed[index].favourites_count = count;
|
||||
this.feed[index].favourited = false;
|
||||
})
|
||||
},
|
||||
|
||||
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')
|
||||
.then(res => {
|
||||
//
|
||||
}).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')
|
||||
.then(res => {
|
||||
//
|
||||
}).catch(err => {
|
||||
this.feed[index].reblogs_count = count;
|
||||
this.feed[index].reblogged = false;
|
||||
})
|
||||
},
|
||||
|
||||
openContextMenu(idx) {
|
||||
this.postIndex = idx;
|
||||
this.showMenu = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.contextMenu.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;
|
||||
}
|
||||
},
|
||||
|
||||
deletePost() {
|
||||
this.feed.splice(this.postIndex, 1);
|
||||
},
|
||||
|
||||
handleReport(post) {
|
||||
this.reportedStatusId = post.id;
|
||||
this.$nextTick(() => {
|
||||
this.reportedStatus = post;
|
||||
this.$refs.reportModal.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();
|
||||
});
|
||||
},
|
||||
|
||||
handleBookmark(index) {
|
||||
let p = this.feed[index];
|
||||
|
||||
axios.post('/i/bookmark', {
|
||||
item: p.id
|
||||
})
|
||||
.then(res => {
|
||||
this.feed[index].bookmarked = !p.bookmarked;
|
||||
})
|
||||
.catch(err => {
|
||||
this.$bvToast.toast('Cannot bookmark post at this time.', {
|
||||
title: 'Bookmark Error',
|
||||
variant: 'danger',
|
||||
autoHideDelay: 5000
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.discover-my-hashtags-component {
|
||||
.bg-stellar {
|
||||
background: #7474BF;
|
||||
background: -webkit-linear-gradient(to right, #348AC7, #7474BF);
|
||||
background: linear-gradient(to right, #348AC7, #7474BF);
|
||||
}
|
||||
.font-default {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
</style>
|
190
resources/assets/components/discover/Insights.vue
Normal file
190
resources/assets/components/discover/Insights.vue
Normal file
|
@ -0,0 +1,190 @@
|
|||
<template>
|
||||
<div class="discover-insights-component">
|
||||
<div v-if="isLoaded" class="container-fluid mt-3">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<sidebar :user="profile" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-6">
|
||||
<b-breadcrumb class="font-default" :items="breadcrumbItems"></b-breadcrumb>
|
||||
|
||||
<h1 class="font-default">Account Insights</h1>
|
||||
<p class="font-default lead">A brief overview of your account</p>
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<div class="card bg-midnight">
|
||||
<div class="card-body font-default text-white">
|
||||
<h1 class="display-4 mb-n2">{{ formatCount(profile.statuses_count) }}</h1>
|
||||
<p class="primary lead mb-0 font-weight-bold">Posts</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-md-6 mb-3">
|
||||
<div class="card bg-midnight">
|
||||
<div class="card-body font-default text-white">
|
||||
<h1 class="display-4 mb-n2">{{ formatCount(profile.followers_count) }}</h1>
|
||||
<p class="primary lead mb-0 font-weight-bold">Followers</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="profile.statuses_count" class="card my-3 bg-midnight">
|
||||
<div class="card-header bg-dark border-bottom border-primary text-white font-default lead">Popular Posts</div>
|
||||
<div v-if="!popularLoaded" class="card-body text-white">
|
||||
<b-spinner/>
|
||||
</div>
|
||||
|
||||
<ul v-else class="list-group list-group-flush font-default text-white">
|
||||
<li v-for="post in popular" class="list-group-item bg-midnight">
|
||||
<div class="media align-items-center">
|
||||
<img
|
||||
v-if="post.media_attachments.length"
|
||||
:src="post.media_attachments[0].url"
|
||||
onerror="this.onerror=null;this.src='/storage/no-preview.png?v=0'"
|
||||
class="media-photo shadow">
|
||||
|
||||
<div class="media-body">
|
||||
<p class="media-caption mb-0">{{ post.content_text.slice(0, 40) }}</p>
|
||||
<p class="mb-0">
|
||||
<span class="font-weight-bold">{{ post.favourites_count }} Likes</span>
|
||||
<span class="mx-2">·</span>
|
||||
<span class="text-muted">Posted {{ timeago(post.created_at) }} ago</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary primary font-weight-bold rounded-pill" @click="gotoPost(post)">View</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Drawer from './../partials/drawer.vue';
|
||||
import Sidebar from './../partials/sidebar.vue';
|
||||
import StatusCard from './../partials/TimelineStatus.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"status-card": StatusCard
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: true,
|
||||
isLoading: true,
|
||||
profile: window._sharedData.user,
|
||||
feed: [],
|
||||
popular: [],
|
||||
popularLoaded: false,
|
||||
breadcrumbItems: [
|
||||
{
|
||||
text: 'Discover',
|
||||
href: '/i/web/discover'
|
||||
},
|
||||
{
|
||||
text: 'Account Insights',
|
||||
active: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchConfig();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
axios.get('/api/pixelfed/v2/discover/meta')
|
||||
.then(res => {
|
||||
if(res.data.insights.enabled == false) {
|
||||
this.$router.push('/i/web/discover');
|
||||
}
|
||||
this.fetchPopular();
|
||||
})
|
||||
},
|
||||
|
||||
fetchPopular() {
|
||||
axios.get('/api/pixelfed/v2/discover/account-insights')
|
||||
.then(res => {
|
||||
this.popular = res.data.filter(p => {
|
||||
return p.favourites_count;
|
||||
});
|
||||
this.popularLoaded = true;
|
||||
})
|
||||
},
|
||||
|
||||
formatCount(val) {
|
||||
return App.util.format.count(val);
|
||||
},
|
||||
|
||||
timeago(ts) {
|
||||
return App.util.format.timeAgo(ts);
|
||||
},
|
||||
|
||||
gotoPost(status) {
|
||||
this.$router.push({
|
||||
name: 'post',
|
||||
path: `/i/web/post/${status.id}`,
|
||||
params: {
|
||||
id: status.id,
|
||||
cachedStatus: status,
|
||||
cachedProfile: this.profile
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.discover-insights-component {
|
||||
.bg-stellar {
|
||||
background: #7474BF;
|
||||
background: -webkit-linear-gradient(to right, #348AC7, #7474BF);
|
||||
background: linear-gradient(to right, #348AC7, #7474BF);
|
||||
}
|
||||
|
||||
.bg-midnight {
|
||||
background: #232526;
|
||||
background: -webkit-linear-gradient(to right, #414345, #232526);
|
||||
background: linear-gradient(to right, #414345, #232526);
|
||||
}
|
||||
|
||||
.font-default {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.media-photo {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 8px;
|
||||
margin-right: 2rem;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.media-caption {
|
||||
letter-spacing: -0.3px;
|
||||
font-size: 17px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
</style>
|
172
resources/assets/components/discover/Memories.vue
Normal file
172
resources/assets/components/discover/Memories.vue
Normal file
|
@ -0,0 +1,172 @@
|
|||
<template>
|
||||
<div class="discover-my-memories web-wrapper">
|
||||
<div v-if="isLoaded" class="container-fluid mt-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<sidebar :user="profile" />
|
||||
</div>
|
||||
|
||||
<div v-if="tabIndex === 0" class="col-md-6 col-lg-6">
|
||||
<b-breadcrumb class="font-default" :items="breadcrumbItems"></b-breadcrumb>
|
||||
|
||||
<h1 class="font-default">My Memories</h1>
|
||||
<p class="font-default lead">Posts from this day in previous years</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<b-spinner v-if="!feedLoaded" />
|
||||
|
||||
<status-card
|
||||
v-for="(post, idx) in feed"
|
||||
:key="'ti0:'+idx+':'+post.id"
|
||||
:profile="profile"
|
||||
:status="post"
|
||||
/>
|
||||
|
||||
<p v-if="feedLoaded && feed.length == 0" class="lead">No memories found :(</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="tabIndex === 1" class="col-md-6 col-lg-6">
|
||||
<b-breadcrumb class="font-default" :items="breadcrumbItems"></b-breadcrumb>
|
||||
|
||||
<h1 class="font-default">My Memories</h1>
|
||||
<p class="font-default lead">Posts I've liked from this day in previous years</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<b-spinner v-if="!likedLoaded" />
|
||||
|
||||
<status-card
|
||||
v-for="(post, idx) in liked"
|
||||
:key="'ti1:'+idx+':'+post.id"
|
||||
:profile="profile"
|
||||
:status="post"
|
||||
/>
|
||||
|
||||
<p v-if="likedLoaded && liked.length == 0" class="lead">No memories found :(</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 col-lg-3">
|
||||
<div class="nav flex-column nav-pills font-default">
|
||||
<a class="nav-link" :class="{ active: tabIndex == 0 }" href="#" @click.prevent="toggleTab(0)">My Posts</a>
|
||||
<a class="nav-link" :class="{ active: tabIndex == 1 }" href="#" @click.prevent="toggleTab(1)">Posts I've Liked</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Drawer from './../partials/drawer.vue';
|
||||
import Sidebar from './../partials/sidebar.vue';
|
||||
import StatusCard from './../partials/TimelineStatus.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"status-card": StatusCard
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: true,
|
||||
profile: window._sharedData.user,
|
||||
curDate: undefined,
|
||||
tabIndex: 0,
|
||||
feedLoaded: false,
|
||||
likedLoaded: false,
|
||||
feed: [],
|
||||
liked: [],
|
||||
breadcrumbItems: [
|
||||
{
|
||||
text: 'Discover',
|
||||
href: '/i/web/discover'
|
||||
},
|
||||
{
|
||||
text: 'My Memories',
|
||||
active: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.curDate = new Date();
|
||||
this.fetchConfig();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
if(
|
||||
window._sharedData.hasOwnProperty('discoverMeta') &&
|
||||
window._sharedData.discoverMeta
|
||||
) {
|
||||
this.config = window._sharedData.discoverMeta;
|
||||
this.isLoaded = true;
|
||||
if(this.config.memories.enabled == false) {
|
||||
this.$router.push('/i/web/discover');
|
||||
} else {
|
||||
this.fetchMemories();
|
||||
}
|
||||
return;
|
||||
}
|
||||
axios.get('/api/pixelfed/v2/discover/meta')
|
||||
.then(res => {
|
||||
this.config = res.data;
|
||||
this.isLoaded = true;
|
||||
window._sharedData.discoverMeta = res.data;
|
||||
if(res.data.memories.enabled == false) {
|
||||
this.$router.push('/i/web/discover');
|
||||
} else {
|
||||
this.fetchMemories();
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
fetchMemories() {
|
||||
axios.get('/api/pixelfed/v2/discover/memories')
|
||||
.then(res => {
|
||||
this.feed = res.data;
|
||||
this.feedLoaded = true;
|
||||
});
|
||||
},
|
||||
|
||||
fetchLiked() {
|
||||
axios.get('/api/pixelfed/v2/discover/memories?type=liked')
|
||||
.then(res => {
|
||||
this.liked = res.data;
|
||||
this.likedLoaded = true;
|
||||
});
|
||||
},
|
||||
|
||||
toggleTab(idx) {
|
||||
if(idx == 1) {
|
||||
if(!this.likedLoaded) {
|
||||
this.fetchLiked();
|
||||
}
|
||||
}
|
||||
this.tabIndex = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.discover-my-memories {
|
||||
.bg-stellar {
|
||||
background: #7474BF;
|
||||
background: -webkit-linear-gradient(to right, #348AC7, #7474BF);
|
||||
background: linear-gradient(to right, #348AC7, #7474BF);
|
||||
}
|
||||
.font-default {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
</style>
|
149
resources/assets/components/discover/ServerFeed.vue
Normal file
149
resources/assets/components/discover/ServerFeed.vue
Normal file
|
@ -0,0 +1,149 @@
|
|||
<template>
|
||||
<div class="discover-serverfeeds-component">
|
||||
<div class="container-fluid mt-3">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<sidebar :user="profile" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-6">
|
||||
<b-breadcrumb class="font-default" :items="breadcrumbItems"></b-breadcrumb>
|
||||
|
||||
<h1 class="font-default">Server Timelines</h1>
|
||||
<p class="font-default lead">Browse timelines of a specific instance</p>
|
||||
|
||||
<hr>
|
||||
|
||||
<b-spinner v-if="isLoading && !initialTab" />
|
||||
|
||||
<status-card
|
||||
v-if="!isLoading"
|
||||
v-for="(post, idx) in feed"
|
||||
:key="'ti1:'+idx+':'+post.id"
|
||||
:profile="profile"
|
||||
:status="post"
|
||||
/>
|
||||
|
||||
<p v-if="!initialTab && !isLoading && feed.length == 0" class="lead">No posts found :(</p>
|
||||
|
||||
<div v-if="initialTab === true">
|
||||
<p v-if="config.server.mode == 'allowlist'" class="lead">Select an instance from the menu</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 col-lg-3">
|
||||
<div v-if="config.server.mode === 'allowlist'" class="nav flex-column nav-pills font-default">
|
||||
<a
|
||||
v-for="(tag, idx) in domains"
|
||||
class="nav-link"
|
||||
:class="{ active: tagIndex == idx }"
|
||||
href="#"
|
||||
@click.prevent="toggleTag(idx)">
|
||||
{{ tag }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Drawer from './../partials/drawer.vue';
|
||||
import Sidebar from './../partials/sidebar.vue';
|
||||
import StatusCard from './../partials/TimelineStatus.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"status-card": StatusCard
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: false,
|
||||
isLoading: true,
|
||||
initialTab: true,
|
||||
config: {},
|
||||
profile: window._sharedData.user,
|
||||
tagIndex: undefined,
|
||||
domains: [],
|
||||
feed: [],
|
||||
breadcrumbItems: [
|
||||
{
|
||||
text: 'Discover',
|
||||
href: '/i/web/discover'
|
||||
},
|
||||
{
|
||||
text: 'Server Timelines',
|
||||
active: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchConfig();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
axios.get('/api/pixelfed/v2/discover/meta')
|
||||
.then(res => {
|
||||
this.config = res.data;
|
||||
if(this.config.server.enabled == false) {
|
||||
this.$router.push('/i/web/discover');
|
||||
}
|
||||
if(this.config.server.mode === 'allowlist') {
|
||||
this.domains = this.config.server.domains.split(',');
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
fetchFeed(domain) {
|
||||
this.isLoading = true;
|
||||
axios.get('/api/pixelfed/v2/discover/server-timeline', {
|
||||
params: {
|
||||
domain: domain
|
||||
}
|
||||
}).then(res => {
|
||||
this.feed = res.data;
|
||||
this.isLoading = false;
|
||||
this.isLoaded = true;
|
||||
})
|
||||
.catch(err => {
|
||||
this.feed = [];
|
||||
this.tagIndex = null;
|
||||
this.isLoaded = true;
|
||||
this.isLoading = false;
|
||||
})
|
||||
},
|
||||
|
||||
toggleTag(tag) {
|
||||
this.initialTab = false;
|
||||
this.tagIndex = tag;
|
||||
this.fetchFeed(this.domains[tag]);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.discover-serverfeeds-component {
|
||||
.bg-stellar {
|
||||
background: #7474BF;
|
||||
background: -webkit-linear-gradient(to right, #348AC7, #7474BF);
|
||||
background: linear-gradient(to right, #348AC7, #7474BF);
|
||||
}
|
||||
.font-default {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
</style>
|
280
resources/assets/components/discover/Settings.vue
Normal file
280
resources/assets/components/discover/Settings.vue
Normal file
|
@ -0,0 +1,280 @@
|
|||
<template>
|
||||
<div class="discover-admin-settings-component">
|
||||
<div v-if="isLoaded" class="container-fluid mt-3">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-lg-3">
|
||||
<sidebar :user="profile" />
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-6">
|
||||
<b-breadcrumb class="font-default" :items="breadcrumbItems"></b-breadcrumb>
|
||||
|
||||
<h1 class="font-default">Discover Settings</h1>
|
||||
<!-- <p class="font-default lead">Browse timelines of a specific instance</p> -->
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="card font-default shadow-none border">
|
||||
<div class="card-header">
|
||||
<p class="text-center font-weight-bold mb-0">Manage Features</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="mb-2">
|
||||
<b-form-checkbox size="lg" v-model="hashtags.enabled" name="check-button" switch class="font-weight-bold">
|
||||
My Hashtags
|
||||
</b-form-checkbox>
|
||||
<p class="text-muted">Allow users to browse timelines of hashtags they follow</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<b-form-checkbox size="lg" v-model="memories.enabled" name="check-button" switch class="font-weight-bold">
|
||||
My Memories
|
||||
</b-form-checkbox>
|
||||
<p class="text-muted">Allow users to access Memories, a timeline of posts they made or liked on this day in past years</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<b-form-checkbox size="lg" v-model="insights.enabled" name="check-button" switch class="font-weight-bold">
|
||||
Account Insights
|
||||
</b-form-checkbox>
|
||||
<p class="text-muted">Allow users to access Account Insights, an overview of their account activity</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<b-form-checkbox size="lg" v-model="friends.enabled" name="check-button" switch class="font-weight-bold">
|
||||
Find Friends
|
||||
</b-form-checkbox>
|
||||
<p class="text-muted">Allow users to access Find Friends, a directory of popular accounts</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<b-form-checkbox size="lg" v-model="server.enabled" name="check-button" switch class="font-weight-bold">
|
||||
Server Timelines
|
||||
</b-form-checkbox>
|
||||
<p class="text-muted">Allow users to access Server Timelines, a timeline of public posts from a specific instance</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="server.enabled" class="card font-default shadow-none border my-3">
|
||||
<div class="card-header">
|
||||
<p class="text-center font-weight-bold mb-0">Manage Server Timelines</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-2">
|
||||
<b-form-group label="Server Mode">
|
||||
<b-form-radio v-model="server.mode" value="all" disabled>Allow any instance (Not Recommended)</b-form-radio>
|
||||
<b-form-radio v-model="server.mode" value="allowlist">Limit by approved domains</b-form-radio>
|
||||
</b-form-group>
|
||||
<p class="text-muted">Set the allowed instances to browse</p>
|
||||
</div>
|
||||
|
||||
<div v-if="server.mode == 'allowlist'">
|
||||
<b-form-group label="Allowed Domains">
|
||||
<b-form-textarea
|
||||
v-model="server.domains"
|
||||
placeholder="Add domains to allow here, separated by commas"
|
||||
rows="3"
|
||||
max-rows="6"
|
||||
></b-form-textarea>
|
||||
</b-form-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 col-lg-3">
|
||||
<button v-if="hasChanged" class="btn btn-primary btn-block primary font-weight-bold" @click="saveFeatures">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
import Drawer from './../partials/drawer.vue';
|
||||
import Sidebar from './../partials/sidebar.vue';
|
||||
import StatusCard from './../partials/TimelineStatus.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
"drawer": Drawer,
|
||||
"sidebar": Sidebar,
|
||||
"status-card": StatusCard
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isLoaded: false,
|
||||
isLoading: true,
|
||||
profile: window._sharedData.user,
|
||||
breadcrumbItems: [
|
||||
{
|
||||
text: 'Discover',
|
||||
href: '/i/web/discover'
|
||||
},
|
||||
{
|
||||
text: 'Settings',
|
||||
active: true
|
||||
}
|
||||
],
|
||||
hasChanged: false,
|
||||
features: {},
|
||||
original: undefined,
|
||||
hashtags: { enabled: undefined },
|
||||
memories: { enabled: undefined },
|
||||
insights: { enabled: undefined },
|
||||
friends: { enabled: undefined },
|
||||
server: { enabled: undefined, mode: 'allowlist', domains: '' },
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
hashtags: {
|
||||
deep: true,
|
||||
handler: function(val, old) {
|
||||
this.updateFeatures('hashtags');
|
||||
},
|
||||
},
|
||||
|
||||
memories: {
|
||||
deep: true,
|
||||
handler: function(val, old) {
|
||||
this.updateFeatures('memories');
|
||||
},
|
||||
},
|
||||
|
||||
insights: {
|
||||
deep: true,
|
||||
handler: function(val, old) {
|
||||
this.updateFeatures('insights');
|
||||
},
|
||||
},
|
||||
|
||||
friends: {
|
||||
deep: true,
|
||||
handler: function(val, old) {
|
||||
this.updateFeatures('friends');
|
||||
},
|
||||
},
|
||||
|
||||
server: {
|
||||
deep: true,
|
||||
handler: function(val, old) {
|
||||
this.updateFeatures('server');
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
beforeMount() {
|
||||
if(!this.profile.is_admin) {
|
||||
this.$router.push('/i/web/discover');
|
||||
}
|
||||
this.fetchConfig();
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchConfig() {
|
||||
axios.get('/api/pixelfed/v2/discover/meta')
|
||||
.then(res => {
|
||||
this.original = res.data;
|
||||
this.storeOriginal(res.data);
|
||||
})
|
||||
},
|
||||
|
||||
storeOriginal(data) {
|
||||
this.friends.enabled = data.friends.enabled;
|
||||
this.hashtags.enabled = data.hashtags.enabled;
|
||||
this.insights.enabled = data.insights.enabled;
|
||||
this.memories.enabled = data.memories.enabled;
|
||||
this.server = {
|
||||
domains: data.server.domains,
|
||||
enabled: data.server.enabled,
|
||||
mode: data.server.mode
|
||||
};
|
||||
this.isLoaded = true;
|
||||
},
|
||||
|
||||
updateFeatures(id) {
|
||||
if(!this.isLoaded) {
|
||||
return;
|
||||
}
|
||||
let changed = false;
|
||||
if(this.friends.enabled !== this.original.friends.enabled) {
|
||||
changed = true;
|
||||
}
|
||||
if(this.hashtags.enabled !== this.original.hashtags.enabled) {
|
||||
changed = true;
|
||||
}
|
||||
if(this.insights.enabled !== this.original.insights.enabled) {
|
||||
changed = true;
|
||||
}
|
||||
if(this.memories.enabled !== this.original.memories.enabled) {
|
||||
changed = true;
|
||||
}
|
||||
if(this.server.enabled !== this.original.server.enabled) {
|
||||
changed = true;
|
||||
}
|
||||
if(this.server.domains !== this.original.server.domains) {
|
||||
changed = true;
|
||||
}
|
||||
if(this.server.mode !== this.original.server.mode) {
|
||||
changed = true;
|
||||
}
|
||||
// if(JSON.stringify(this.server) !== JSON.stringify(this.original.server)) {
|
||||
// changed = true;
|
||||
// }
|
||||
this.hasChanged = changed;
|
||||
},
|
||||
|
||||
saveFeatures() {
|
||||
axios.post('/api/pixelfed/v2/discover/admin/features', {
|
||||
features: {
|
||||
friends: this.friends,
|
||||
hashtags: this.hashtags,
|
||||
insights: this.insights,
|
||||
memories: this.memories,
|
||||
server: this.server
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
// let data = {
|
||||
// friends: res.data.friends,
|
||||
// hashtags: res.data.hashtags,
|
||||
// insights: res.data.insights,
|
||||
// memories: res.data.memories,
|
||||
// server: res.data.server
|
||||
// }
|
||||
// this.original = data;
|
||||
this.server = res.data.server;
|
||||
this.$bvToast.toast('Successfully updated settings!', {
|
||||
title: 'Discover Settings',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: true,
|
||||
variant: 'success'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.discover-admin-settings-component {
|
||||
.bg-stellar {
|
||||
background: #7474BF;
|
||||
background: -webkit-linear-gradient(to right, #348AC7, #7474BF);
|
||||
background: linear-gradient(to right, #348AC7, #7474BF);
|
||||
}
|
||||
.font-default {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
letter-spacing: -0.7px;
|
||||
}
|
||||
|
||||
.active {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue