Daniel Supernault 2 лет назад
Родитель
Сommit
8efb4047b1

+ 377 - 343
resources/assets/components/partials/post/PostHeader.vue

@@ -1,348 +1,382 @@
 <template>
-	<div class="card-header border-0" style="border-top-left-radius: 15px;border-top-right-radius: 15px;">
-		<div class="media align-items-center">
-			<a :href="status.account.url" @click.prevent="goToProfile()" style="margin-right: 10px;">
-				<img :src="getStatusAvatar()" style="border-radius:15px;" width="44" height="44" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
-			</a>
-
-			<div class="media-body">
-				<p class="font-weight-bold username">
-					<a :href="status.account.url" class="text-dark" :id="'apop_'+status.id" @click.prevent="goToProfile">
-						{{ status.account.acct }}
-					</a>
-					<b-popover :target="'apop_'+status.id" triggers="hover" placement="bottom" custom-class="shadow border-0 rounded-px">
-						<profile-hover-card
-							:profile="status.account"
-							v-on:follow="follow"
-							v-on:unfollow="unfollow" />
-					</b-popover>
-				</p>
-				<p class="text-lighter mb-0" style="font-size: 13px;">
-					<span v-if="status.account.is_admin" class="d-none d-md-inline-block">
-						<span class="badge badge-light text-danger user-select-none" title="Admin account">ADMIN</span>
-						<span class="mx-1 text-lighter">·</span>
-					</span>
-					<a class="timestamp text-lighter" :href="status.url" @click.prevent="goToPost()" :title="status.created_at">
-						{{ timeago(status.created_at) }}
-					</a>
-
-					<span v-if="config.ab.pue && status.hasOwnProperty('edited_at') && status.edited_at">
-						<span class="mx-1 text-lighter">·</span>
-						<a class="text-lighter" href="#" @click.prevent="openEditModal">Edited</a>
-					</span>
-
-					<span class="mx-1 text-lighter">·</span>
-					<span class="visibility text-lighter" :title="scopeTitle(status.visibility)"><i :class="scopeIcon(status.visibility)"></i></span>
-
-					<span v-if="status.place && status.place.hasOwnProperty('name')" class="d-none d-md-inline-block">
-						<span class="mx-1 text-lighter">·</span>
-						<span class="location text-lighter"><i class="far fa-map-marker-alt"></i> {{ status.place.name }}, {{ status.place.country }}</span>
-					</span>
-				</p>
-			</div>
-
-			<button v-if="!useDropdownMenu" class="btn btn-link text-lighter" @click="openMenu">
-				<i class="far fa-ellipsis-v fa-lg"></i>
-			</button>
-
-			<b-dropdown
-				v-else
-				no-caret
-				right
-				variant="link"
-				toggle-class="text-lighter"
-				html="<i class='far fa-ellipsis-v fa-lg px-3'></i>"
-				>
-				<b-dropdown-item>
-					<p class="mb-0 font-weight-bold">{{ $t('menu.viewPost') }}</p>
-				</b-dropdown-item>
-				<b-dropdown-item>
-					<p class="mb-0 font-weight-bold">{{ $t('common.copyLink') }}</p>
-				</b-dropdown-item>
-				<b-dropdown-item v-if="status.local">
-					<p class="mb-0 font-weight-bold">{{ $t('menu.embed') }}</p>
-				</b-dropdown-item>
-				<b-dropdown-divider v-if="!owner"></b-dropdown-divider>
-				<b-dropdown-item v-if="!owner">
-					<p class="mb-0 font-weight-bold">{{ $t('menu.report') }}</p>
-					<p class="small text-muted mb-0">Report content that violate our rules</p>
-				</b-dropdown-item>
-				<b-dropdown-item v-if="!owner && status.hasOwnProperty('relationship')">
-					<p class="mb-0 font-weight-bold">{{ status.relationship.muting ? 'Unmute' : 'Mute' }}</p>
-					<p class="small text-muted mb-0">Hide posts from this account in your feeds</p>
-				</b-dropdown-item>
-				<b-dropdown-item v-if="!owner && status.hasOwnProperty('relationship')">
-					<p class="mb-0 font-weight-bold text-danger">{{ status.relationship.blocking ? 'Unblock' : 'Block' }}</p>
-					<p class="small text-muted mb-0">Restrict all content from this account</p>
-				</b-dropdown-item>
-				<b-dropdown-divider v-if="owner || admin"></b-dropdown-divider>
-				<b-dropdown-item v-if="owner || admin">
-					<p class="mb-0 font-weight-bold text-danger">
-						{{ $t('common.delete') }}
-					</p>
-				</b-dropdown-item>
-			</b-dropdown>
-		</div>
-
-		<edit-history-modal ref="editModal" :status="status" />
-	</div>
+    <div>
+        <div v-if="isReblog" class="card-header bg-light border-0" style="border-top-left-radius: 15px;border-top-right-radius: 15px;">
+            <div class="media align-items-center" style="height:10px;">
+                <a :href="reblogAccount.url" class="mx-2" @click.prevent="goToProfileById(reblogAccount.id)">
+                    <img :src="reblogAccount.avatar" style="border-radius:10px;" width="24" height="24" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
+                </a>
+                <div style="font-size:12px;font-weight:bold">
+                    <i class="far fa-retweet text-warning mr-1"></i> Reblogged by <a :href="reblogAccount.url" class="text-dark" @click.prevent="goToProfileById(reblogAccount.id)">&commat;{{ reblogAccount.acct }}</a>
+                </div>
+            </div>
+        </div>
+        <div class="card-header border-0" style="border-top-left-radius: 15px;border-top-right-radius: 15px;">
+            <div class="media align-items-center">
+                <a :href="status.account.url" @click.prevent="goToProfile()" style="margin-right: 10px;">
+                    <img :src="getStatusAvatar()" style="border-radius:15px;" width="44" height="44" onerror="this.onerror=null;this.src='/storage/avatars/default.png?v=0';">
+                </a>
+
+                <div class="media-body">
+                    <p class="font-weight-bold username">
+                        <a :href="status.account.url" class="text-dark" :id="'apop_'+status.id" @click.prevent="goToProfile">
+                            {{ status.account.acct }}
+                        </a>
+                        <b-popover :target="'apop_'+status.id" triggers="hover" placement="bottom" custom-class="shadow border-0 rounded-px">
+                            <profile-hover-card
+                                :profile="status.account"
+                                v-on:follow="follow"
+                                v-on:unfollow="unfollow" />
+                        </b-popover>
+                    </p>
+                    <p class="text-lighter mb-0" style="font-size: 13px;">
+                        <span v-if="status.account.is_admin" class="d-none d-md-inline-block">
+                            <span class="badge badge-light text-danger user-select-none" title="Admin account">ADMIN</span>
+                            <span class="mx-1 text-lighter">·</span>
+                        </span>
+                        <a class="timestamp text-lighter" :href="status.url" @click.prevent="goToPost()" :title="status.created_at">
+                            {{ timeago(status.created_at) }}
+                        </a>
+
+                        <span v-if="config.ab.pue && status.hasOwnProperty('edited_at') && status.edited_at">
+                            <span class="mx-1 text-lighter">·</span>
+                            <a class="text-lighter" href="#" @click.prevent="openEditModal">Edited</a>
+                        </span>
+
+                        <span class="mx-1 text-lighter">·</span>
+                        <span class="visibility text-lighter" :title="scopeTitle(status.visibility)"><i :class="scopeIcon(status.visibility)"></i></span>
+
+                        <span v-if="status.place && status.place.hasOwnProperty('name')" class="d-none d-md-inline-block">
+                            <span class="mx-1 text-lighter">·</span>
+                            <span class="location text-lighter"><i class="far fa-map-marker-alt"></i> {{ status.place.name }}, {{ status.place.country }}</span>
+                        </span>
+                    </p>
+                </div>
+
+                <button v-if="!useDropdownMenu" class="btn btn-link text-lighter" @click="openMenu">
+                    <i class="far fa-ellipsis-v fa-lg"></i>
+                </button>
+
+                <b-dropdown
+                    v-else
+                    no-caret
+                    right
+                    variant="link"
+                    toggle-class="text-lighter"
+                    html="<i class='far fa-ellipsis-v fa-lg px-3'></i>"
+                    >
+                    <b-dropdown-item>
+                        <p class="mb-0 font-weight-bold">{{ $t('menu.viewPost') }}</p>
+                    </b-dropdown-item>
+                    <b-dropdown-item>
+                        <p class="mb-0 font-weight-bold">{{ $t('common.copyLink') }}</p>
+                    </b-dropdown-item>
+                    <b-dropdown-item v-if="status.local">
+                        <p class="mb-0 font-weight-bold">{{ $t('menu.embed') }}</p>
+                    </b-dropdown-item>
+                    <b-dropdown-divider v-if="!owner"></b-dropdown-divider>
+                    <b-dropdown-item v-if="!owner">
+                        <p class="mb-0 font-weight-bold">{{ $t('menu.report') }}</p>
+                        <p class="small text-muted mb-0">Report content that violate our rules</p>
+                    </b-dropdown-item>
+                    <b-dropdown-item v-if="!owner && status.hasOwnProperty('relationship')">
+                        <p class="mb-0 font-weight-bold">{{ status.relationship.muting ? 'Unmute' : 'Mute' }}</p>
+                        <p class="small text-muted mb-0">Hide posts from this account in your feeds</p>
+                    </b-dropdown-item>
+                    <b-dropdown-item v-if="!owner && status.hasOwnProperty('relationship')">
+                        <p class="mb-0 font-weight-bold text-danger">{{ status.relationship.blocking ? 'Unblock' : 'Block' }}</p>
+                        <p class="small text-muted mb-0">Restrict all content from this account</p>
+                    </b-dropdown-item>
+                    <b-dropdown-divider v-if="owner || admin"></b-dropdown-divider>
+                    <b-dropdown-item v-if="owner || admin">
+                        <p class="mb-0 font-weight-bold text-danger">
+                            {{ $t('common.delete') }}
+                        </p>
+                    </b-dropdown-item>
+                </b-dropdown>
+            </div>
+
+            <edit-history-modal ref="editModal" :status="status" />
+        </div>
+    </div>
 </template>
 
 <script type="text/javascript">
-	import ProfileHoverCard from './../profile/ProfileHoverCard.vue';
-	import EditHistoryModal from './EditHistoryModal.vue';
-
-	export default {
-		props: {
-			status: {
-				type: Object
-			},
-
-			profile: {
-				type: Object
-			},
-
-			useDropdownMenu: {
-				type: Boolean,
-				default: false
-			}
-		},
-
-		components: {
-			"profile-hover-card": ProfileHoverCard,
-			"edit-history-modal": EditHistoryModal
-		},
-
-		data() {
-			return {
-				config: window.App.config,
-				menuLoading: true,
-				owner: false,
-				admin: false,
-				license: false
-			}
-		},
-
-		methods: {
-			timeago(ts) {
-				let short = App.util.format.timeAgo(ts);
-				if(
-					short.endsWith('s') ||
-					short.endsWith('m') ||
-					short.endsWith('h')
-				) {
-					return short;
-				}
-				const intl = new Intl.DateTimeFormat(undefined, {
-					year:  'numeric',
-					month: 'short',
-					day:   'numeric',
-					hour: 'numeric',
-					minute: 'numeric'
-				});
-				return intl.format(new Date(ts));
-			},
-
-			openMenu() {
-				this.$emit('menu');
-			},
-
-			scopeIcon(scope) {
-				switch(scope) {
-					case 'public':
-						return 'far fa-globe';
-					break;
-
-					case 'unlisted':
-						return 'far fa-lock-open';
-					break;
-
-					case 'private':
-						return 'far fa-lock';
-					break;
-
-					default:
-						return 'far fa-globe';
-					break;
-				}
-			},
-
-			scopeTitle(scope) {
-				switch(scope) {
-					case 'public':
-						return 'Visible to everyone';
-					break;
-
-					case 'unlisted':
-						return 'Hidden from public feeds';
-					break;
-
-					case 'private':
-						return 'Only visible to followers';
-					break;
-
-					default:
-						return '';
-					break;
-				}
-			},
-
-			goToPost() {
-				if(location.pathname.split('/').pop() == this.status.id) {
-					location.href = this.status.local ? this.status.url + '?fs=1' : this.status.url;
-					return;
-				}
-
-				this.$router.push({
-					name: 'post',
-					path: `/i/web/post/${this.status.id}`,
-					params: {
-						id: this.status.id,
-						cachedStatus: this.status,
-						cachedProfile: this.profile
-					}
-				})
-			},
-
-			goToProfile() {
-				this.$nextTick(() => {
-					this.$router.push({
-						name: 'profile',
-						path: `/i/web/profile/${this.status.account.id}`,
-						params: {
-							id: this.status.account.id,
-							cachedProfile: this.status.account,
-							cachedUser: this.profile
-						}
-					});
-				});
-			},
-
-			toggleContentWarning() {
-				this.key++;
-				this.sensitive = true;
-				this.status.sensitive = !this.status.sensitive;
-			},
-
-			like() {
-				event.currentTarget.blur();
-				if(this.status.favourited) {
-					this.$emit('unlike');
-				} else {
-					this.$emit('like');
-				}
-			},
-
-			toggleMenu(bvEvent) {
-				setTimeout(() => {
-					this.menuLoading = false;
-				}, 500);
-			},
-
-			closeMenu(bvEvent) {
-				setTimeout(() => {
-					bvEvent.target.parentNode.firstElementChild.blur();
-				}, 100);
-			},
-
-			showLikes() {
-				event.currentTarget.blur();
-				this.$emit('likes-modal');
-			},
-
-			showShares() {
-				event.currentTarget.blur();
-				this.$emit('shares-modal');
-			},
-
-			showComments() {
-				event.currentTarget.blur();
-				this.showCommentDrawer = !this.showCommentDrawer;
-			},
-
-			copyLink() {
-				event.currentTarget.blur();
-				App.util.clipboard(this.status.url);
-			},
-
-			shareToOther() {
-				if (navigator.canShare) {
-					navigator.share({
-						url: this.status.url
-					})
-					.then(() => console.log('Share was successful.'))
-					.catch((error) => console.log('Sharing failed', error));
-				} else {
-					swal('Not supported', 'Your current device does not support native sharing.', 'error');
-				}
-			},
-
-			counterChange(type) {
-				this.$emit('counter-change', type);
-			},
-
-			showCommentLikes(post) {
-				this.$emit('comment-likes-modal', post);
-			},
-
-			shareStatus() {
-				this.$emit('share');
-			},
-
-			unshareStatus() {
-				this.$emit('unshare');
-			},
-
-			handleReport(post) {
-				this.$emit('handle-report', post);
-			},
-
-			follow() {
-				this.$emit('follow');
-			},
-
-			unfollow() {
-				this.$emit('unfollow');
-			},
-
-			handleReblog() {
-				this.isReblogging = true;
-				if(this.status.reblogged) {
-					this.$emit('unshare');
-				} else {
-					this.$emit('share');
-				}
-
-				setTimeout(() => {
-					this.isReblogging = false;
-				}, 5000);
-			},
-
-			handleBookmark() {
-				event.currentTarget.blur();
-				this.isBookmarking = true;
-				this.$emit('bookmark');
-
-				setTimeout(() => {
-					this.isBookmarking = false;
-				}, 5000);
-			},
-
-			getStatusAvatar() {
-				if(window._sharedData.user.id == this.status.account.id) {
-					return window._sharedData.user.avatar;
-				}
-
-				return this.status.account.avatar;
-			},
-
-			openModTools() {
-				this.$emit('mod-tools');
-			},
-
-			openEditModal() {
-				this.$refs.editModal.open();
-			}
-		}
-	}
+    import ProfileHoverCard from './../profile/ProfileHoverCard.vue';
+    import EditHistoryModal from './EditHistoryModal.vue';
+
+    export default {
+        props: {
+            status: {
+                type: Object
+            },
+
+            profile: {
+                type: Object
+            },
+
+            useDropdownMenu: {
+                type: Boolean,
+                default: false
+            },
+
+            isReblog: {
+                type: Boolean,
+                default: false
+            },
+
+            reblogAccount: {
+                type: Object
+            }
+        },
+
+        components: {
+            "profile-hover-card": ProfileHoverCard,
+            "edit-history-modal": EditHistoryModal
+        },
+
+        data() {
+            return {
+                config: window.App.config,
+                menuLoading: true,
+                owner: false,
+                admin: false,
+                license: false
+            }
+        },
+
+        methods: {
+            timeago(ts) {
+                let short = App.util.format.timeAgo(ts);
+                if(
+                    short.endsWith('s') ||
+                    short.endsWith('m') ||
+                    short.endsWith('h')
+                ) {
+                    return short;
+                }
+                const intl = new Intl.DateTimeFormat(undefined, {
+                    year:  'numeric',
+                    month: 'short',
+                    day:   'numeric',
+                    hour: 'numeric',
+                    minute: 'numeric'
+                });
+                return intl.format(new Date(ts));
+            },
+
+            openMenu() {
+                this.$emit('menu');
+            },
+
+            scopeIcon(scope) {
+                switch(scope) {
+                    case 'public':
+                        return 'far fa-globe';
+                    break;
+
+                    case 'unlisted':
+                        return 'far fa-lock-open';
+                    break;
+
+                    case 'private':
+                        return 'far fa-lock';
+                    break;
+
+                    default:
+                        return 'far fa-globe';
+                    break;
+                }
+            },
+
+            scopeTitle(scope) {
+                switch(scope) {
+                    case 'public':
+                        return 'Visible to everyone';
+                    break;
+
+                    case 'unlisted':
+                        return 'Hidden from public feeds';
+                    break;
+
+                    case 'private':
+                        return 'Only visible to followers';
+                    break;
+
+                    default:
+                        return '';
+                    break;
+                }
+            },
+
+            goToPost() {
+                if(location.pathname.split('/').pop() == this.status.id) {
+                    location.href = this.status.local ? this.status.url + '?fs=1' : this.status.url;
+                    return;
+                }
+
+                this.$router.push({
+                    name: 'post',
+                    path: `/i/web/post/${this.status.id}`,
+                    params: {
+                        id: this.status.id,
+                        cachedStatus: this.status,
+                        cachedProfile: this.profile
+                    }
+                })
+            },
+
+            goToProfileById(id) {
+                this.$nextTick(() => {
+                    this.$router.push({
+                        name: 'profile',
+                        path: `/i/web/profile/${id}`,
+                        params: {
+                            id: id,
+                            cachedUser: this.profile
+                        }
+                    });
+                });
+            },
+
+            goToProfile() {
+                this.$nextTick(() => {
+                    this.$router.push({
+                        name: 'profile',
+                        path: `/i/web/profile/${this.status.account.id}`,
+                        params: {
+                            id: this.status.account.id,
+                            cachedProfile: this.status.account,
+                            cachedUser: this.profile
+                        }
+                    });
+                });
+            },
+
+            toggleContentWarning() {
+                this.key++;
+                this.sensitive = true;
+                this.status.sensitive = !this.status.sensitive;
+            },
+
+            like() {
+                event.currentTarget.blur();
+                if(this.status.favourited) {
+                    this.$emit('unlike');
+                } else {
+                    this.$emit('like');
+                }
+            },
+
+            toggleMenu(bvEvent) {
+                setTimeout(() => {
+                    this.menuLoading = false;
+                }, 500);
+            },
+
+            closeMenu(bvEvent) {
+                setTimeout(() => {
+                    bvEvent.target.parentNode.firstElementChild.blur();
+                }, 100);
+            },
+
+            showLikes() {
+                event.currentTarget.blur();
+                this.$emit('likes-modal');
+            },
+
+            showShares() {
+                event.currentTarget.blur();
+                this.$emit('shares-modal');
+            },
+
+            showComments() {
+                event.currentTarget.blur();
+                this.showCommentDrawer = !this.showCommentDrawer;
+            },
+
+            copyLink() {
+                event.currentTarget.blur();
+                App.util.clipboard(this.status.url);
+            },
+
+            shareToOther() {
+                if (navigator.canShare) {
+                    navigator.share({
+                        url: this.status.url
+                    })
+                    .then(() => console.log('Share was successful.'))
+                    .catch((error) => console.log('Sharing failed', error));
+                } else {
+                    swal('Not supported', 'Your current device does not support native sharing.', 'error');
+                }
+            },
+
+            counterChange(type) {
+                this.$emit('counter-change', type);
+            },
+
+            showCommentLikes(post) {
+                this.$emit('comment-likes-modal', post);
+            },
+
+            shareStatus() {
+                this.$emit('share');
+            },
+
+            unshareStatus() {
+                this.$emit('unshare');
+            },
+
+            handleReport(post) {
+                this.$emit('handle-report', post);
+            },
+
+            follow() {
+                this.$emit('follow');
+            },
+
+            unfollow() {
+                this.$emit('unfollow');
+            },
+
+            handleReblog() {
+                this.isReblogging = true;
+                if(this.status.reblogged) {
+                    this.$emit('unshare');
+                } else {
+                    this.$emit('share');
+                }
+
+                setTimeout(() => {
+                    this.isReblogging = false;
+                }, 5000);
+            },
+
+            handleBookmark() {
+                event.currentTarget.blur();
+                this.isBookmarking = true;
+                this.$emit('bookmark');
+
+                setTimeout(() => {
+                    this.isBookmarking = false;
+                }, 5000);
+            },
+
+            getStatusAvatar() {
+                if(window._sharedData.user.id == this.status.account.id) {
+                    return window._sharedData.user.avatar;
+                }
+
+                return this.status.account.avatar;
+            },
+
+            openModTools() {
+                this.$emit('mod-tools');
+            },
+
+            openEditModal() {
+                this.$refs.editModal.open();
+            }
+        }
+    }
 </script>

+ 109 - 11
resources/assets/components/sections/Timeline.vue

@@ -8,6 +8,30 @@
         </div>
 
         <div v-else>
+            <transition name="fade">
+                <div v-if="showReblogBanner && getScope() === 'home'" class="card bg-g-amin card-body shadow-sm mb-3" style="border-radius: 15px;">
+                    <div class="d-flex justify-content-around align-items-center">
+                        <div class="flex-grow-1 ft-std">
+                            <h2 class="font-weight-bold text-white mb-0">Introducing Reblogs in feeds</h2>
+                            <hr />
+                            <p class="lead text-white mb-0">
+                                See reblogs from accounts you follow in your home feed!
+                            </p>
+                            <p class="text-white small mb-1" style="opacity:0.6">
+                                You can disable reblogs in feeds on the Timeline Settings page.
+                            </p>
+                            <hr />
+                            <div class="d-flex">
+                                <button class="btn btn-light rounded-pill font-weight-bold btn-block mr-2" @click.prevent="enableReblogs()">
+                                    <template v-if="!enablingReblogs">Show reblogs in home feed</template>
+                                    <b-spinner small v-else />
+                                </button>
+                                <button class="btn btn-outline-light rounded-pill font-weight-bold px-5" @click.prevent="hideReblogs()">Hide</button>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </transition>
             <status
                 v-for="(status, index) in feed"
                 :key="'pf_feed:' + status.id + ':idx:' + index + ':fui:' + forceUpdateIdx"
@@ -140,6 +164,7 @@
 
         data() {
             return {
+                settings: [],
                 isLoaded: false,
                 feed: [],
                 ids: [],
@@ -159,7 +184,9 @@
                 reportedStatusId: 0,
                 showSharesModal: false,
                 sharesModalPost: {},
-                forceUpdateIdx: 0
+                forceUpdateIdx: 0,
+                showReblogBanner: false,
+                enablingReblogs: false
             }
         },
 
@@ -174,7 +201,7 @@
                     return;
                 };
             }
-            this.fetchTimeline();
+            this.fetchSettings();
         },
 
         methods: {
@@ -194,13 +221,48 @@
                 }
             },
 
+            fetchSettings() {
+                axios.get('/api/pixelfed/v1/web/settings')
+                .then(res => {
+                    this.settings = res.data;
+
+                    if(!res.data) {
+                        this.showReblogBanner = true;
+                    } else {
+                        if(res.data.hasOwnProperty('hide_reblog_banner')) {
+                        } else if(res.data.hasOwnProperty('enable_reblogs')) {
+                            if(!res.data.enable_reblogs) {
+                                this.showReblogBanner = true;
+                            }
+                        } else {
+                            this.showReblogBanner = true;
+                        }
+                    }
+                })
+                .finally(() => {
+                    this.fetchTimeline();
+                })
+            },
+
             fetchTimeline(scrollToTop = false) {
-                let url = `/api/pixelfed/v1/timelines/${this.getScope()}`;
-                axios.get(url, {
-                    params: {
+                let url, params;
+                if(this.getScope() === 'home' && this.settings && this.settings.hasOwnProperty('enable_reblogs') && this.settings.enable_reblogs) {
+                    url = `/api/v1/timelines/home`;
+                    params = {
+                        '_pe': 1,
+                        max_id: this.max_id,
+                        limit: 6,
+                        include_reblogs: true,
+                    }
+                } else {
+                    url = `/api/pixelfed/v1/timelines/${this.getScope()}`;
+                    params = {
                         max_id: this.max_id,
-                        limit: 6
+                        limit: 6,
                     }
+                }
+                axios.get(url, {
+                    params: params
                 }).then(res => {
                     let ids = res.data.map(p => {
                         if(p && p.hasOwnProperty('relationship')) {
@@ -242,12 +304,24 @@
 
                 this.isFetchingMore = true;
 
-                let url = `/api/pixelfed/v1/timelines/${this.getScope()}`;
-                axios.get(url, {
-                    params: {
+                let url, params;
+                if(this.getScope() === 'home' && this.settings && this.settings.hasOwnProperty('enable_reblogs') && this.settings.enable_reblogs) {
+                    url = `/api/v1/timelines/home`;
+                    params = {
+                        '_pe': 1,
+                        max_id: this.max_id,
+                        limit: 6,
+                        include_reblogs: true,
+                    }
+                } else {
+                    url = `/api/pixelfed/v1/timelines/${this.getScope()}`;
+                    params = {
                         max_id: this.max_id,
-                        limit: 6
+                        limit: 6,
                     }
+                }
+                axios.get(url, {
+                    params: params
                 }).then(res => {
                     if(!res.data.length) {
                         this.endFeedReached = true;
@@ -568,7 +642,31 @@
                 this.$nextTick(() => {
                     this.forceUpdateIdx++;
                 });
-            }
+            },
+
+            enableReblogs() {
+                this.enablingReblogs = true;
+
+                axios.post('/api/pixelfed/v1/web/settings', {
+                    field: 'enable_reblogs',
+                    value: true
+                })
+                .then(res => {
+                    setTimeout(() => {
+                        window.location.reload();
+                    }, 1000);
+                })
+            },
+
+            hideReblogs() {
+                this.showReblogBanner = false;
+                axios.post('/api/pixelfed/v1/web/settings', {
+                    field: 'hide_reblog_banner',
+                    value: true
+                })
+                .then(res => {
+                })
+            },
         },
 
         watch: {

+ 238 - 228
resources/assets/sass/spa.scss

@@ -1,166 +1,172 @@
 @import "lib/ibmplexsans";
 
 :root {
-	--light: #fff;
-	--dark: #000;
-	--body-bg: rgba(243,244,246,1);
-	--body-color: #212529;
-	--nav-bg: #fff;
-	--bg-light: #f8f9fa;
-
-	--primary: #3B82F6;
-	--light-gray: #f8f9fa;
-	--text-lighter: #94a3b8;
-
-	--card-bg: #fff;
-	--light-hover-bg: #f9fafb;
-	--btn-light-border: #fff;
-	--input-border: #e2e8f0;
-	--comment-bg: #eff2f5;
-	--border-color: #dee2e6;
-	--card-header-accent: #f9fafb;
-
-	--dropdown-item-hover-bg: #e9ecef;
-	--dropdown-item-hover-color: #16181b;
-	--dropdown-item-color: #64748b;
-	--dropdown-item-active-color: #334155;
+    --light: #fff;
+    --dark: #000;
+    --body-bg: rgba(243,244,246,1);
+    --body-color: #212529;
+    --nav-bg: #fff;
+    --bg-light: #f8f9fa;
+
+    --primary: #3B82F6;
+    --light-gray: #f8f9fa;
+    --text-lighter: #94a3b8;
+
+    --card-bg: #fff;
+    --light-hover-bg: #f9fafb;
+    --btn-light-border: #fff;
+    --input-border: #e2e8f0;
+    --comment-bg: #eff2f5;
+    --border-color: #dee2e6;
+    --card-header-accent: #f9fafb;
+
+    --dropdown-item-hover-bg: #e9ecef;
+    --dropdown-item-hover-color: #16181b;
+    --dropdown-item-color: #64748b;
+    --dropdown-item-active-color: #334155;
 }
 
 @media (prefers-color-scheme: dark) {
-	:root {
-		--light: #000;
-		--dark: #fff;
-		--body-bg: #000;
-		--body-color: #9ca3af;
-		--nav-bg: #000;
-		--bg-light: #212124;
-
-		--light-gray: #212124;
-		--text-lighter: #818181;
-
-		--card-bg: #161618;
-		--light-hover-bg: #212124;
-		--btn-light-border: #161618;
-		--input-border: #161618;
-		--comment-bg: #212124;
-		--border-color: #212124;
-		--card-header-accent: #212124;
-
-		--dropdown-item-hover-bg: #000;
-		--dropdown-item-hover-color: #818181;
-		--dropdown-item-color: #64748b;
-		--dropdown-item-active-color: #fff;
-	}
+    :root {
+        --light: #000;
+        --dark: #fff;
+        --body-bg: #000;
+        --body-color: #9ca3af;
+        --nav-bg: #000;
+        --bg-light: #212124;
+
+        --light-gray: #212124;
+        --text-lighter: #818181;
+
+        --card-bg: #161618;
+        --light-hover-bg: #212124;
+        --btn-light-border: #161618;
+        --input-border: #161618;
+        --comment-bg: #212124;
+        --border-color: #212124;
+        --card-header-accent: #212124;
+
+        --dropdown-item-hover-bg: #000;
+        --dropdown-item-hover-color: #818181;
+        --dropdown-item-color: #64748b;
+        --dropdown-item-active-color: #fff;
+    }
 }
 
 .force-light-mode {
-	--light: #fff;
-	--dark: #000;
-	--body-bg: rgba(243,244,246,1);
-	--body-color: #212529;
-	--nav-bg: #fff;
-	--bg-light: #f8f9fa;
-
-	--primary: #3B82F6;
-	--light-gray: #f8f9fa;
-	--text-lighter: #94a3b8;
-
-	--card-bg: #fff;
-	--light-hover-bg: #f9fafb;
-	--btn-light-border: #fff;
-	--input-border: #e2e8f0;
-	--comment-bg: #eff2f5;
-	--border-color: #dee2e6;
-	--card-header-accent: #f9fafb;
-
-	--dropdown-item-hover-bg: #e9ecef;
-	--dropdown-item-hover-color: #16181b;
-	--dropdown-item-color: #64748b;
-	--dropdown-item-active-color: #334155;
+    --light: #fff;
+    --dark: #000;
+    --body-bg: rgba(243,244,246,1);
+    --body-color: #212529;
+    --nav-bg: #fff;
+    --bg-light: #f8f9fa;
+
+    --primary: #3B82F6;
+    --light-gray: #f8f9fa;
+    --text-lighter: #94a3b8;
+
+    --card-bg: #fff;
+    --light-hover-bg: #f9fafb;
+    --btn-light-border: #fff;
+    --input-border: #e2e8f0;
+    --comment-bg: #eff2f5;
+    --border-color: #dee2e6;
+    --card-header-accent: #f9fafb;
+
+    --dropdown-item-hover-bg: #e9ecef;
+    --dropdown-item-hover-color: #16181b;
+    --dropdown-item-color: #64748b;
+    --dropdown-item-active-color: #334155;
 }
 
 .force-dark-mode {
-	--light: #000;
-	--dark: #fff;
-	--body-bg: #000;
-	--body-color: #9ca3af;
-	--nav-bg: #000;
-	--bg-light: #212124;
-
-	--light-gray: #212124;
-	--text-lighter: #818181;
-
-	--card-bg: #161618;
-	--light-hover-bg: #212124;
-	--btn-light-border: #161618;
-	--input-border: #161618;
-	--comment-bg: #212124;
-	--border-color: #212124;
-	--card-header-accent: #212124;
-
-	--dropdown-item-hover-bg: #000;
-	--dropdown-item-hover-color: #818181;
-	--dropdown-item-color: #64748b;
-	--dropdown-item-active-color: #b3b3b3;
+    --light: #000;
+    --dark: #fff;
+    --body-bg: #000;
+    --body-color: #9ca3af;
+    --nav-bg: #000;
+    --bg-light: #212124;
+
+    --light-gray: #212124;
+    --text-lighter: #818181;
+
+    --card-bg: #161618;
+    --light-hover-bg: #212124;
+    --btn-light-border: #161618;
+    --input-border: #161618;
+    --comment-bg: #212124;
+    --border-color: #212124;
+    --card-header-accent: #212124;
+
+    --dropdown-item-hover-bg: #000;
+    --dropdown-item-hover-color: #818181;
+    --dropdown-item-color: #64748b;
+    --dropdown-item-active-color: #b3b3b3;
 }
 
 body {
-	background: var(--body-bg);
-	font-family: 'IBM Plex Sans', sans-serif;
-	color: var(--body-color);
+    background: var(--body-bg);
+    font-family: 'IBM Plex Sans', sans-serif;
+    color: var(--body-color);
 }
 
 .web-wrapper {
-	margin-bottom: 10rem;
+    margin-bottom: 10rem;
 }
 
 .container-fluid {
-	max-width: 1440px !important;
+    max-width: 1440px !important;
 }
 
 .jumbotron {
-	border-radius: 18px;
+    border-radius: 18px;
 }
 
 .rounded-px {
-	border-radius: 18px;
+    border-radius: 18px;
 }
 
 .doc-body {
-	p:last-child {
-		margin-bottom: 0;
-	}
+    p:last-child {
+        margin-bottom: 0;
+    }
 }
 
 .navbar-laravel {
-	background-color: var(--nav-bg);
+    background-color: var(--nav-bg);
 }
 
 .sticky-top {
-	z-index: 2;
+    z-index: 2;
 }
 
 .navbar-light .navbar-brand {
-	color: var(--dark);
+    color: var(--dark);
 
-	&:hover {
-		color: var(--dark);
-	}
+    &:hover {
+        color: var(--dark);
+    }
 }
 
 .primary {
-	color: var(--primary);
+    color: var(--primary);
+}
+
+.bg-g-amin {
+    background: #8E2DE2;
+    background: -webkit-linear-gradient(to right, #4A00E0, #8E2DE2);
+    background: linear-gradient(to left, #4A00E0, #8E2DE2);
 }
 
 .text-lighter {
-	color: var(--text-lighter) !important;
+    color: var(--text-lighter) !important;
 }
 
 .text-dark {
     color: var(--body-color) !important;
 
     &:hover {
-    	color: var(--dark) !important;
+        color: var(--dark) !important;
     }
 }
 
@@ -169,16 +175,16 @@ a.text-dark:hover {
 }
 
 .badge-primary {
-	background-color: var(--primary);
+    background-color: var(--primary);
 }
 
 .btn-primary {
-	background-color: var(--primary);
-	color: #fff !important;
+    background-color: var(--primary);
+    color: #fff !important;
 }
 
 .btn-outline-light {
-	border-color: var(--light-gray);
+    border-color: var(--light-gray);
 }
 
 .border {
@@ -187,51 +193,51 @@ a.text-dark:hover {
 
 .bg-white,
 .bg-light {
-	background-color: var(--bg-light) !important;
-	border-color: var(--bg-light) !important;
+    background-color: var(--bg-light) !important;
+    border-color: var(--bg-light) !important;
 }
 
 .btn-light {
-	background-color: var(--light-gray);
-	border-color: var(--btn-light-border);
-	color: var(--body-color);
+    background-color: var(--light-gray);
+    border-color: var(--btn-light-border);
+    color: var(--body-color);
 
-	&:hover {
-		color: var(--body-color);
-		background-color: var(--card-bg);
-		border-color: var(--btn-light-border);
-	}
+    &:hover {
+        color: var(--body-color);
+        background-color: var(--card-bg);
+        border-color: var(--btn-light-border);
+    }
 }
 
 .autocomplete-input {
-	border: 1px solid var(--light-gray) !important;
-	color: var(--body-color);
+    border: 1px solid var(--light-gray) !important;
+    color: var(--body-color);
 }
 
 .autocomplete-result-list {
-	background: var(--light) !important;
-	z-index: 2 !important;
+    background: var(--light) !important;
+    z-index: 2 !important;
 }
 
 .dropdown-menu,
 span.twitter-typeahead .tt-menu,
 .form-control {
-	border: 1px solid var(--border-color) !important;
-	color: var(--body-color);
-	background-color: var(--card-bg);
+    border: 1px solid var(--border-color) !important;
+    color: var(--body-color);
+    background-color: var(--card-bg);
 }
 
 .tribute-container li,
 .dropdown-item,
 span.twitter-typeahead .tt-suggestion {
-	color: var(--body-color);
+    color: var(--body-color);
 }
 
 .dropdown-item:hover,
 span.twitter-typeahead .tt-suggestion:hover,
 .dropdown-item:focus,
 span.twitter-typeahead .tt-suggestion:focus {
-	color: var(--dropdown-item-hover-color);
+    color: var(--dropdown-item-hover-color);
     background-color: var(--dropdown-item-hover-bg);
     text-decoration: none;
 }
@@ -245,7 +251,7 @@ span.twitter-typeahead .tt-suggestion:focus {
 .card-header,
 .card-footer,
 .ph-item {
-	background-color: var(--card-bg);
+    background-color: var(--card-bg);
 }
 
 .badge-light,
@@ -253,143 +259,147 @@ span.twitter-typeahead .tt-suggestion:focus {
 .ph-avatar,
 .ph-picture,
 .ph-row div {
-	background-color: var(--light-gray);
+    background-color: var(--light-gray);
 }
 
 .card-header,
 .border-top,
 .border-bottom {
-	border-color: var(--border-color) !important;
+    border-color: var(--border-color) !important;
 }
 
 .modal-header,
 .modal-footer {
-	border-color: var(--border-color);
+    border-color: var(--border-color);
 }
 
 .compose-action:hover {
-	background-color: var(--light-gray) !important;
+    background-color: var(--light-gray) !important;
 }
 
 .dropdown-divider {
-	border-color: var(--dropdown-item-hover-bg);
+    border-color: var(--dropdown-item-hover-bg);
 }
 
 .metro-nav {
-	&.flex-column {
-		background-color: var(--card-bg);
-
-		.nav-item {
-			.nav-link:hover {
-				background-color: var(--light-hover-bg);
-			}
-		}
-	}
+    &.flex-column {
+        background-color: var(--card-bg);
+
+        .nav-item {
+            .nav-link:hover {
+                background-color: var(--light-hover-bg);
+            }
+        }
+    }
 }
 
 .child-reply-form {
-	.form-control {
-		border-color: var(--input-border);
-		color: var(--body-color);
-	}
+    .form-control {
+        border-color: var(--input-border);
+        color: var(--body-color);
+    }
 }
 
 .ui-menu {
-	.btn-group {
-		.btn:first-child {
-			border-top-left-radius: 50rem;
-			border-bottom-left-radius: 50rem;
-		}
-
-		.btn:last-child {
-			border-top-right-radius: 50rem;
-			border-bottom-right-radius: 50rem;
-		}
-
-		.btn-primary {
-			font-weight: bold;
-		}
-	}
+    .btn-group {
+        .btn:first-child {
+            border-top-left-radius: 50rem;
+            border-bottom-left-radius: 50rem;
+        }
+
+        .btn:last-child {
+            border-top-right-radius: 50rem;
+            border-bottom-right-radius: 50rem;
+        }
+
+        .btn-primary {
+            font-weight: bold;
+        }
+    }
 
-	.b-custom-control-lg {
-		padding-bottom: 8px;
-	}
+    .b-custom-control-lg {
+        padding-bottom: 8px;
+    }
 }
 
 .content-label {
-	&-wrapper {
-		div:not(.content-label) {
-			height: 100%;
-		}
-	}
+    &-wrapper {
+        div:not(.content-label) {
+            height: 100%;
+        }
+    }
 
-	&-text {
-		width: 80%;
-		@media (min-width: 768px) {
-			width: 50%;
-		}
-	}
+    &-text {
+        width: 80%;
+        @media (min-width: 768px) {
+            width: 50%;
+        }
+    }
 }
 
 .compose-modal-component {
-	.form-control:focus {
-    	color: var(--body-color);
+    .form-control:focus {
+        color: var(--body-color);
     }
 }
 
 .modal-body {
-	.nav-tabs .nav-link.active,
-	.nav-tabs .nav-item.show .nav-link {
-		background-color: transparent;
-		border-color: var(--border-color);
-	}
-
-	.nav-tabs .nav-link:hover,
-	.nav-tabs .nav-link:focus {
-		border-color: var(--border-color);
-	}
-
-	.form-control:focus {
-    	color: var(--body-color);
+    .nav-tabs .nav-link.active,
+    .nav-tabs .nav-item.show .nav-link {
+        background-color: transparent;
+        border-color: var(--border-color);
+    }
+
+    .nav-tabs .nav-link:hover,
+    .nav-tabs .nav-link:focus {
+        border-color: var(--border-color);
+    }
+
+    .form-control:focus {
+        color: var(--body-color);
     }
 }
 
 .tribute-container {
-	border: 0;
-
-	ul {
-		margin-top: 0;
-		border-color: var(--border-color);
-	}
+    border: 0;
 
-	li {
-		padding: 0.5rem 1rem;
-		border-top: 0;
-		border-left: 0;
-		border-right: 0;
-		font-size: 13px;
+    ul {
+        margin-top: 0;
+        border-color: var(--border-color);
+    }
 
-		&:not(:last-child) {
-			border-bottom: 1px solid var(--border-color);
-		}
+    li {
+        padding: 0.5rem 1rem;
+        border-top: 0;
+        border-left: 0;
+        border-right: 0;
+        font-size: 13px;
+
+        &:not(:last-child) {
+            border-bottom: 1px solid var(--border-color);
+        }
+
+        &.highlight,
+        &:hover {
+            color: var(--body-color);
+            font-weight: bold;
+            background: rgba(44, 120, 191, 0.25);
+        }
+    }
+}
 
-		&.highlight,
-		&:hover {
-    		color: var(--body-color);
-			font-weight: bold;
-			background: rgba(44, 120, 191, 0.25);
-		}
-	}
+.ft-std {
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
 }
 
 .timeline-status-component {
-	.username {
-		font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
-		margin-bottom: -3px;
-		word-break: break-word;
-
-		@media (min-width: 768px) {
-			font-size: 17px;
-		}
-	}
+    .username {
+        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+        margin-bottom: -3px;
+        word-break: break-word;
+
+        @media (min-width: 768px) {
+            font-size: 17px;
+        }
+    }
 }