Prechádzať zdrojové kódy

Add Discover components

Daniel Supernault 2 rokov pred
rodič
commit
b447db082f

+ 405 - 0
resources/assets/components/Discover.vue

@@ -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 - 0
resources/assets/components/discover/FindFriends.vue

@@ -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 - 0
resources/assets/components/discover/Hashtags.vue

@@ -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 - 0
resources/assets/components/discover/Insights.vue

@@ -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 - 0
resources/assets/components/discover/Memories.vue

@@ -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 - 0
resources/assets/components/discover/ServerFeed.vue

@@ -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 - 0
resources/assets/components/discover/Settings.vue

@@ -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>