1
0
Эх сурвалжийг харах

Update PostComponent, add reply modal

Daniel Supernault 4 жил өмнө
parent
commit
a10d851fb7

+ 327 - 88
resources/assets/js/components/PostComponent.vue

@@ -35,24 +35,10 @@
           </div>
           </div>
           <div v-if="user != false" class="float-right">
           <div v-if="user != false" class="float-right">
             <div class="post-actions">
             <div class="post-actions">
-            <div class="dropdown">
-              <button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
-              <span class="fas fa-ellipsis-v text-muted"></span>
+            <div>
+              <button class="btn btn-link text-dark no-caret" title="Post options" @click="ctxMenu()">
+                <span class="fas fa-ellipsis-v text-muted"></span>
               </button>
               </button>
-              <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
-                  <a class="dropdown-item font-weight-bold" @click="copyPostUrl()">Copy Post Url</a>
-                  <a class="dropdown-item font-weight-bold" @click="showEmbedPostModal()">Embed</a>
-                  <div v-if="!owner()">
-                    <a class="dropdown-item font-weight-bold" :href="reportUrl()">Report</a>
-                    <a class="dropdown-item font-weight-bold" v-on:click="muteProfile()">Mute Profile</a>
-                    <a class="dropdown-item font-weight-bold" v-on:click="blockProfile()">Block Profile</a>
-                  </div>
-                  <div v-if="ownerOrAdmin()">
-                    <a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
-                    <a v-if="canEdit" class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a>
-                    <a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost(status)">Delete</a>
-                  </div>
-                </div>
               </div>
               </div>
             </div>
             </div>
           </div>
           </div>
@@ -108,30 +94,16 @@
               </div>
               </div>
               <div class="float-right">
               <div class="float-right">
                 <div class="post-actions">
                 <div class="post-actions">
-                <div v-if="user != false" class="dropdown">
-                  <button class="btn btn-link text-dark no-caret dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" title="Post options">
-                  <span class="fas fa-ellipsis-v text-muted"></span>
-                  </button>
-                      <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
-                        <a class="dropdown-item font-weight-bold" @click="copyPostUrl()">Copy Post Url</a>
-                        <a class="dropdown-item font-weight-bold" @click="showEmbedPostModal()">Embed</a>
-                        <span v-if="!owner()">
-                          <a class="dropdown-item font-weight-bold" :href="reportUrl()">Report</a>
-                          <a class="dropdown-item font-weight-bold" v-on:click="muteProfile">Mute Profile</a>
-                          <a class="dropdown-item font-weight-bold" v-on:click="blockProfile">Block Profile</a>
-                        </span>
-                        <span v-if="ownerOrAdmin()">
-                          <a class="dropdown-item font-weight-bold" href="#" v-on:click.prevent="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</a>
-                          <a v-if="canEdit" class="dropdown-item font-weight-bold" :href="editUrl()">Edit</a>
-                          <a class="dropdown-item font-weight-bold text-danger" v-on:click="deletePost">Delete</a>
-                        </span>
-                      </div>
+                  <div v-if="user != false">
+                    <button class="btn btn-link text-dark no-caret" title="Post options" @click="ctxMenu()">
+                      <span class="fas fa-ellipsis-v text-muted"></span>
+                    </button>
                   </div>
                   </div>
                 </div>
                 </div>
               </div>
               </div>
             </div>
             </div>
             <div class="d-flex flex-md-column flex-column-reverse h-100" style="overflow-y: auto;">
             <div class="d-flex flex-md-column flex-column-reverse h-100" style="overflow-y: auto;">
-              <div class="card-body status-comments pb-5 pt-0">
+              <div class="card-body status-comments pt-0">
                 <div class="status-comment">
                 <div class="status-comment">
                   <div v-if="status.content.length" class="pt-3">
                   <div v-if="status.content.length" class="pt-3">
                     <div v-if="showCaption != true">
                     <div v-if="showCaption != true">
@@ -227,7 +199,12 @@
 
 
                 </div>
                 </div>
               </div>
               </div>
-              <div class="card-body flex-grow-0 py-1">
+              <div v-if="reactionBarLoading" class="card-body flex-grow-0 py-4 text-center">
+                <div class="spinner-border" role="status">
+                  <span class="sr-only">Loading...</span>
+                </div>
+              </div>
+              <div v-else class="card-body flex-grow-0 py-1">
                 <div v-if="loaded && user.hasOwnProperty('id')" class="reactions my-2 pb-1 d-flex justify-content-between">
                 <div v-if="loaded && user.hasOwnProperty('id')" class="reactions my-2 pb-1 d-flex justify-content-between">
                   <h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger mr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3>
                   <h3 v-bind:class="[reactions.liked ? 'fas fa-heart text-danger mr-3 m-0 cursor-pointer' : 'far fa-heart pr-3 m-0 like-btn cursor-pointer']" title="Like" v-on:click="likeStatus"></h3>
                   <h3 v-if="!status.comments_disabled" class="far fa-comment mr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3>
                   <h3 v-if="!status.comments_disabled" class="far fa-comment mr-3 m-0 cursor-pointer" title="Comment" v-on:click="replyFocus(status)"></h3>
@@ -252,18 +229,13 @@
                 </div>
                 </div>
               </div>
               </div>
             </div>
             </div>
-           <!-- <div v-if="showComments && user.length !== 0" class="card-footer bg-white px-2 py-0">
-              <ul class="nav align-items-center emoji-reactions" style="overflow-x: scroll;flex-wrap: unset;">
-                <li class="nav-item" v-on:click="emojiReaction" v-for="e in emoji">{{e}}</li>
-              </ul>
-            </div> -->
             <div v-if="showComments" class="card-footer bg-white sticky-md-bottom p-0">
             <div v-if="showComments" class="card-footer bg-white sticky-md-bottom p-0">
               <div v-if="user.length == 0" class="comment-form-guest p-3">
               <div v-if="user.length == 0" class="comment-form-guest p-3">
                 <a href="/login">Login</a> to like or comment.
                 <a href="/login">Login</a> to like or comment.
               </div>
               </div>
               <form v-else class="border-0 rounded-0 align-middle" method="post" action="/i/comment" :data-id="statusId" data-truncate="false">
               <form v-else class="border-0 rounded-0 align-middle" method="post" action="/i/comment" :data-id="statusId" data-truncate="false">
-                <textarea class="form-control border-0 rounded-0" name="comment" placeholder="Add a comment…" autocomplete="off" autocorrect="off" style="height:56px;line-height: 18px;max-height:80px;resize: none; padding-right:4.2rem;" v-model="replyText"></textarea>
-                <input type="button" value="Post" class="d-inline-block btn btn-link font-weight-bold reply-btn text-decoration-none" v-on:click.prevent="postReply" :disabled="replyText.length == 0" />
+                <textarea class="form-control border-0 rounded-0" name="comment" placeholder="Add a comment…" autocomplete="off" autocorrect="off" style="height:56px;line-height: 18px;max-height:80px;resize: none; padding-right:4.2rem;" @click="replyFocus(status)"></textarea>
+                <input type="button" value="Post" class="d-inline-block btn btn-link font-weight-bold reply-btn text-decoration-none" disabled/>
               </form>
               </form>
             </div>
             </div>
           </div>
           </div>
@@ -271,9 +243,6 @@
         </div>
         </div>
       </div>
       </div>
       <div class="container" v-if="showProfileMorePosts">
       <div class="container" v-if="showProfileMorePosts">
-        <!-- <div class="py-4">
-          <hr>
-        </div> -->
         <p class="text-lighter px-3 mt-5" style="font-weight: 600;font-size: 15px;">More posts from <a :href="'/'+statusUsername" class="text-dark">{{this.statusUsername}}</a></p>
         <p class="text-lighter px-3 mt-5" style="font-weight: 600;font-size: 15px;">More posts from <a :href="'/'+statusUsername" class="text-dark">{{this.statusUsername}}</a></p>
         <div class="profile-timeline mt-md-4">
         <div class="profile-timeline mt-md-4">
           <div class="row">
           <div class="row">
@@ -474,6 +443,7 @@
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
+
   <b-modal ref="likesModal"
   <b-modal ref="likesModal"
     id="l-modal"
     id="l-modal"
     hide-footer
     hide-footer
@@ -512,9 +482,9 @@
     hide-footer
     hide-footer
     centered
     centered
     title="Shares"
     title="Shares"
-    body-class="list-group-flush p-0">
+    body-class="list-group-flush py-3 px-0">
     <div class="list-group">
     <div class="list-group">
-      <div class="list-group-item border-0" v-for="(user, index) in shares" :key="'modal_shares_'+index">
+      <div class="list-group-item border-0 py-1" v-for="(user, index) in shares" :key="'modal_shares_'+index">
         <div class="media">
         <div class="media">
           <a :href="user.url">
           <a :href="user.url">
             <img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + '’s avatar'" width="30px">
             <img class="mr-3 rounded-circle box-shadow" :src="user.avatar" :alt="user.username + '’s avatar'" width="30px">
@@ -615,6 +585,88 @@
     </div>
     </div>
     <p class="mb-0 text-center small text-muted font-weight-bold"><a href="/site/kb/tagging-people">Learn more</a> about Tagging People.</p>
     <p class="mb-0 text-center small text-muted font-weight-bold"><a href="/site/kb/tagging-people">Learn more</a> about Tagging People.</p>
   </b-modal>
   </b-modal>
+  <b-modal ref="ctxModal"
+    id="ctx-modal"
+    hide-header
+    hide-footer
+    centered
+    rounded
+    size="sm"
+    body-class="list-group-flush p-0 rounded">
+    <div class="list-group text-center">
+      <div v-if="user && user.id != status.account.id && relationship && relationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuUnfollow()">Unfollow</div>
+      <div v-if="user && user.id != status.account.id && relationship && !relationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-primary" @click="ctxMenuFollow()">Follow</div>
+      <div v-if="status && status.local == true" class="list-group-item rounded cursor-pointer" @click="showEmbedPostModal()">Embed</div>
+      <div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">Copy Link</div>
+      <div v-if="status && user.id == status.account.id" class="list-group-item rounded cursor-pointer" @click="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</div>
+      <a v-if="status && user.id == status.account.id" class="list-group-item rounded cursor-pointer text-dark text-decoration-none" :href="editUrl()">Edit</a>
+      <div v-if="user && user.is_admin == true" class="list-group-item rounded cursor-pointer" @click="ctxModMenu()">ModTools</div>
+      <div v-if="status && user.id != status.account.id && !relationship.blocking && !user.is_admin" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="blockProfile()">Block</div>
+      <div v-if="status && user.id != status.account.id && relationship.blocking && !user.is_admin" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="unblockProfile()">Unblock</div>
+      <a v-if="user && user.id != status.account.id && !user.is_admin" class="list-group-item rounded cursor-pointer font-weight-bold text-danger text-decoration-none" :href="reportUrl()">Report</a>
+      <div v-if="status && (user.is_admin || user.id == status.account.id)" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="deletePost(ctxMenuStatus)">Delete</div>
+      <div class="list-group-item rounded cursor-pointer text-lighter" @click="closeCtxMenu()">Cancel</div>
+    </div>
+  </b-modal>
+  <b-modal ref="ctxModModal"
+    id="ctx-mod-modal"
+    hide-header
+    hide-footer
+    centered
+    rounded
+    size="sm"
+    body-class="list-group-flush p-0 rounded">
+    <div class="list-group text-center">
+      <div class="list-group-item rounded cursor-pointer" @click="toggleCommentVisibility">{{ showComments ? 'Disable' : 'Enable'}} Comments</div>
+
+      <div class="list-group-item rounded cursor-pointer" @click="moderatePost('unlist')">Unlist from Timelines</div>
+      <div v-if="status.sensitive" class="list-group-item rounded cursor-pointer" @click="moderatePost('remcw')">Remove Content Warning</div>
+      <div v-else class="list-group-item rounded cursor-pointer" @click="moderatePost('addcw')">Add Content Warning</div>
+      <div class="list-group-item rounded cursor-pointer text-lighter" @click="ctxModMenuClose()">Cancel</div>
+    </div>
+  </b-modal>
+  <b-modal ref="replyModal"
+    id="ctx-reply-modal"
+    hide-footer
+    centered
+    rounded
+    :title-html="replyingToUsername ? 'Reply to <span class=text-dark>' + replyingToUsername + '</span>' : ''"
+    title-tag="p"
+    title-class="font-weight-bold text-muted"
+    size="md"
+    body-class="p-2 rounded">
+    <div>
+      <textarea class="form-control" rows="4" style="border: none; font-size: 18px; resize: none; white-space: pre-wrap;outline: none;" placeholder="Reply here ..." v-model="replyText">
+      </textarea>
+
+      <div class="border-top border-bottom my-2">
+        <ul class="nav align-items-center emoji-reactions" style="overflow-x: scroll;flex-wrap: unset;">
+          <li class="nav-item" v-on:click="emojiReaction(status)" v-for="e in emoji">{{e}}</li>
+        </ul>
+      </div>
+      <div class="d-flex justify-content-between align-items-center">
+        <div>
+          <span class="pl-2 small text-muted font-weight-bold text-monospace">
+            <span :class="[replyText.length > config.uploader.max_caption_length ? 'text-danger':'text-dark']">{{replyText.length > config.uploader.max_caption_length ? config.uploader.max_caption_length - replyText.length : replyText.length}}</span>/{{config.uploader.max_caption_length}}
+          </span>
+        </div>
+        <div class="d-flex align-items-center">
+          <div class="custom-control custom-switch mr-3">
+            <input type="checkbox" class="custom-control-input" id="replyModalCWSwitch" v-model="replySensitive">
+            <label :class="[replySensitive ? 'custom-control-label font-weight-bold text-dark':'custom-control-label text-lighter']" for="replyModalCWSwitch">Mark as NSFW</label>
+          </div>
+          <!-- <select class="custom-select custom-select-sm my-0 mr-2">
+            <option value="public" selected="">Public</option>
+            <option value="unlisted">Unlisted</option>
+            <option value="followers">Followers Only</option>
+          </select> -->
+          <button class="btn btn-primary btn-sm py-2 px-4 lead text-uppercase font-weight-bold" v-on:click.prevent="postReply()" :disabled="replyText.length == 0">
+          {{replySending == true ? 'POSTING' : 'POST'}}
+        </button>
+        </div>
+      </div>
+    </div>
+  </b-modal>
 </div>
 </div>
 </template>
 </template>
 
 
@@ -742,6 +794,7 @@ export default {
             loaded: false,
             loaded: false,
             loading: null,
             loading: null,
             replyingToId: this.statusId,
             replyingToId: this.statusId,
+            replyingToUsername: this.statusUsername,
             replyToIndex: 0,
             replyToIndex: 0,
             replySending: false,
             replySending: false,
             emoji: window.App.util.emoji,
             emoji: window.App.util.emoji,
@@ -753,9 +806,10 @@ export default {
             ctxEmbedShowLikes: false,
             ctxEmbedShowLikes: false,
             ctxEmbedCompactMode: false,
             ctxEmbedCompactMode: false,
             layout: this.profileLayout,
             layout: this.profileLayout,
-            canEdit: false,
             showProfileMorePosts: false,
             showProfileMorePosts: false,
-            profileMorePosts: []
+            profileMorePosts: [],
+            replySending: false,
+            reactionBarLoading: true,
           }
           }
     },
     },
     watch: {
     watch: {
@@ -811,16 +865,6 @@ export default {
     },
     },
 
 
     methods: {
     methods: {
-      showMuteBlock() {
-        let sid = this.status.account.id;
-        let uid = this.user.id;
-        if(sid == uid) {
-          $('.post-actions .menu-author').removeClass('d-none');
-        } else {
-          $('.post-actions .menu-user').removeClass('d-none');
-        }
-      },
-
       reportUrl() {
       reportUrl() {
         return '/i/report?type=post&id=' + this.status.id;
         return '/i/report?type=post&id=' + this.status.id;
       },
       },
@@ -839,33 +883,20 @@ export default {
           axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId)
           axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId)
             .then(response => {
             .then(response => {
                 self.status = response.data.status;
                 self.status = response.data.status;
-                self.user = response.data.user;
-                window._sharedData.curUser = self.user;
-                window.App.util.navatar();
                 self.media = self.status.media_attachments;
                 self.media = self.status.media_attachments;
-                self.reactions = response.data.reactions;
-                self.likes = response.data.likes;
-                self.shares = response.data.shares;
                 self.likesPage = 2;
                 self.likesPage = 2;
                 self.sharesPage = 2;
                 self.sharesPage = 2;
-                this.showMuteBlock();
                 self.showCaption = !response.data.status.sensitive;
                 self.showCaption = !response.data.status.sensitive;
                 if(self.status.comments_disabled == false) {
                 if(self.status.comments_disabled == false) {
                   self.showComments = true;
                   self.showComments = true;
                   this.fetchComments();
                   this.fetchComments();
                 }
                 }
-                if(this.ownerOrAdmin()) {
-                  let od = new Date(this.status.created_at).getTime() + (1 * 24 * 60 * 60 * 1000);
-                  let now = new Date().getTime();
-                  if(od > now) {
-                    this.canEdit = true;
-                  }
-                }
                 this.loaded = true;
                 this.loaded = true;
                 setTimeout(function() {
                 setTimeout(function() {
                   self.fetchProfilePosts();
                   self.fetchProfilePosts();
                 }, 3000);
                 }, 3000);
                 setTimeout(function() {
                 setTimeout(function() {
+                  self.fetchState();
                   document.querySelectorAll('.status-comment .comment-text a').forEach(function(i, e) { 
                   document.querySelectorAll('.status-comment .comment-text a').forEach(function(i, e) { 
                     if(i.href.startsWith(window.location.origin)) {
                     if(i.href.startsWith(window.location.origin)) {
                       return;
                       return;
@@ -882,6 +913,20 @@ export default {
             });
             });
       },
       },
 
 
+      fetchState() {
+        let self = this;
+        axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId+'/state')
+        .then(res => {
+          self.user = res.data.user;
+          window._sharedData.curUser = self.user;
+          window.App.util.navatar();
+          self.likes = res.data.likes;
+          self.shares = res.data.shares;
+          self.reactions = res.data.reactions;
+          self.reactionBarLoading = false;
+        });
+      },
+
       likesModal() {
       likesModal() {
         if($('body').hasClass('loggedIn') == false) {
         if($('body').hasClass('loggedIn') == false) {
           window.location.href = '/login?next=' + encodeURIComponent('/p/' + this.status.shortcode);
           window.location.href = '/login?next=' + encodeURIComponent('/p/' + this.status.shortcode);
@@ -890,14 +935,31 @@ export default {
         if(this.status.favourites_count == 0) {
         if(this.status.favourites_count == 0) {
           return;
           return;
         }
         }
-        this.$refs.likesModal.show();
+        if(this.likes.length) {
+          this.$refs.likesModal.show();
+          return;
+        }
+        axios.get('/api/v2/likes/profile/'+this.statusUsername+'/status/'+this.statusId)
+        .then(res => {
+          this.likes = res.data.data;
+          this.$refs.likesModal.show();
+        });
       },
       },
 
 
       sharesModal() {
       sharesModal() {
         if(this.status.reblogs_count == 0 || $('body').hasClass('loggedIn') == false) {
         if(this.status.reblogs_count == 0 || $('body').hasClass('loggedIn') == false) {
+          window.location.href = '/login?next=' + encodeURIComponent('/p/' + this.status.shortcode);
+          return;
+        }
+        if(this.shares.length) {
+          this.$refs.sharesModal.show();
           return;
           return;
         }
         }
-        this.$refs.sharesModal.show();
+        axios.get('/api/v2/shares/profile/'+this.statusUsername+'/status/'+this.statusId)
+        .then(res => {
+          this.shares = res.data.data;
+          this.$refs.sharesModal.show();
+        });
       },
       },
 
 
       infiniteLikesHandler($state) {
       infiniteLikesHandler($state) {
@@ -1010,31 +1072,35 @@ export default {
         });
         });
       },
       },
 
 
-      muteProfile() {
+      blockProfile() {
         if($('body').hasClass('loggedIn') == false) {
         if($('body').hasClass('loggedIn') == false) {
           return;
           return;
         }
         }
 
 
-        axios.post('/i/mute', {
+        axios.post('/i/block', {
           type: 'user',
           type: 'user',
           item: this.status.account.id
           item: this.status.account.id
         }).then(res => {
         }).then(res => {
-          swal('Success', 'You have successfully muted ' + this.status.account.acct, 'success');
+          this.$refs.ctxModal.hide();
+          this.relationship.blocking = true;
+          swal('Success', 'You have successfully blocked ' + this.status.account.acct, 'success');
         }).catch(err => {
         }).catch(err => {
           swal('Error', 'Something went wrong. Please try again later.', 'error');
           swal('Error', 'Something went wrong. Please try again later.', 'error');
         });
         });
       },
       },
 
 
-      blockProfile() {
+      unblockProfile() {
         if($('body').hasClass('loggedIn') == false) {
         if($('body').hasClass('loggedIn') == false) {
           return;
           return;
         }
         }
 
 
-        axios.post('/i/block', {
+        axios.post('/i/unblock', {
           type: 'user',
           type: 'user',
           item: this.status.account.id
           item: this.status.account.id
         }).then(res => {
         }).then(res => {
-          swal('Success', 'You have successfully blocked ' + this.status.account.acct, 'success');
+          this.relationship.blocking = false;
+          this.$refs.ctxModal.hide();
+          swal('Success', 'You have successfully unblocked ' + this.status.account.acct, 'success');
         }).catch(err => {
         }).catch(err => {
           swal('Error', 'Something went wrong. Please try again later.', 'error');
           swal('Error', 'Something went wrong. Please try again later.', 'error');
         });
         });
@@ -1082,6 +1148,7 @@ export default {
 
 
       postReply() {
       postReply() {
         let self = this;
         let self = this;
+        this.replySending = true;
         if(this.replyText.length == 0 ||
         if(this.replyText.length == 0 ||
           this.replyText.trim() == '@'+this.status.account.acct) {
           this.replyText.trim() == '@'+this.status.account.acct) {
           self.replyText = null;
           self.replyText = null;
@@ -1106,7 +1173,7 @@ export default {
               self.results.unshift(entity);
               self.results.unshift(entity);
             }
             }
             let elem = $('.status-comments')[0];
             let elem = $('.status-comments')[0];
-            elem.scrollTop = elem.clientHeight;
+            elem.scrollTop = elem.clientHeight * 2;
           } else {
           } else {
             if(self.replyToIndex >= 0) {
             if(self.replyToIndex >= 0) {
               let el = self.results[self.replyToIndex];
               let el = self.results[self.replyToIndex];
@@ -1114,6 +1181,8 @@ export default {
               el.reply_count = el.reply_count + 1;
               el.reply_count = el.reply_count + 1;
             }
             }
           }
           }
+          self.$refs.replyModal.hide();
+          self.replySending = false;
         });
         });
       },
       },
 
 
@@ -1147,15 +1216,25 @@ export default {
       },
       },
 
 
       replyFocus(e, index, prependUsername = false) {
       replyFocus(e, index, prependUsername = false) {
+          if($('body').hasClass('loggedIn') == false) {
+            this.redirect('/login?next=' + encodeURIComponent(window.location.pathname));
+            return;
+          }
+          
+          if(this.status.comments_disabled) {
+            return;
+          }
+
           this.replyToIndex = index;
           this.replyToIndex = index;
           this.replyingToId = e.id;
           this.replyingToId = e.id;
+          this.replyingToUsername = e.account.username;
           this.reply_to_profile_id = e.account.id;
           this.reply_to_profile_id = e.account.id;
           let username = e.account.local ? '@' + e.account.username + ' '
           let username = e.account.local ? '@' + e.account.username + ' '
             : '@' + e.account.acct + ' ';
             : '@' + e.account.acct + ' ';
           if(prependUsername == true) {
           if(prependUsername == true) {
             this.replyText = username;
             this.replyText = username;
           }
           }
-          $('textarea[name="comment"]').focus();
+          this.$refs.replyModal.show();
       },
       },
 
 
       fetchComments() {
       fetchComments() {
@@ -1289,7 +1368,9 @@ export default {
             item: self.status.id,
             item: self.status.id,
             disableComments: false
             disableComments: false
           }).then(function(res) {
           }).then(function(res) {
-              window.location.href = self.status.url;
+              self.status.comments_disabled = false;
+              self.$refs.ctxModal.hide();
+              window.location.reload();
           }).catch(function(err) {
           }).catch(function(err) {
             return;
             return;
           });
           });
@@ -1299,8 +1380,9 @@ export default {
             item: self.status.id,
             item: self.status.id,
             disableComments: true
             disableComments: true
           }).then(function(res) {
           }).then(function(res) {
-            self.status.comments_disabled = false;
+            self.status.comments_disabled = true;
             self.showComments = false;
             self.showComments = false;
+            self.$refs.ctxModal.hide();
           }).catch(function(err) {
           }).catch(function(err) {
             return;
             return;
           });
           });
@@ -1374,6 +1456,7 @@ export default {
       showEmbedPostModal() {
       showEmbedPostModal() {
         let mode = this.ctxEmbedCompactMode ? 'compact' : 'full';
         let mode = this.ctxEmbedCompactMode ? 'compact' : 'full';
         this.ctxEmbedPayload = window.App.util.embed.post(this.status.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
         this.ctxEmbedPayload = window.App.util.embed.post(this.status.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
+        this.$refs.ctxModal.hide();
         this.$refs.embedModal.show();
         this.$refs.embedModal.show();
       },
       },
 
 
@@ -1461,10 +1544,166 @@ export default {
             swal('An Error Occurred', 'Please try again later.', 'error');  
             swal('An Error Occurred', 'Please try again later.', 'error');  
         });
         });
       },
       },
+
       copyPostUrl() {
       copyPostUrl() {
         navigator.clipboard.writeText(this.statusUrl);
         navigator.clipboard.writeText(this.statusUrl);
         return;
         return;
-      }
+      },
+
+      moderatePost(action, $event) {
+        let status = this.status;
+        let username = status.account.username;
+        let msg = '';
+        let self = this;
+        switch(action) {
+          case 'addcw':
+            msg = 'Are you sure you want to add a content warning to this post?';
+            swal({
+              title: 'Confirm',
+              text: msg,
+              icon: 'warning',
+              buttons: true,
+              dangerMode: true
+            }).then(res =>  {
+              if(res) {
+                axios.post('/api/v2/moderator/action', {
+                  action: action,
+                  item_id: status.id,
+                  item_type: 'status'
+                }).then(res => {
+                  swal('Success', 'Successfully added content warning', 'success');
+                  status.sensitive = true;
+                  self.ctxModMenuClose();
+                }).catch(err => {
+                  swal(
+                    'Error',
+                    'Something went wrong, please try again later.',
+                    'error'
+                  );
+                  self.ctxModMenuClose();
+                });
+              }
+            });
+          break;
+
+          case 'remcw':
+            msg = 'Are you sure you want to remove the content warning on this post?';
+            swal({
+              title: 'Confirm',
+              text: msg,
+              icon: 'warning',
+              buttons: true,
+              dangerMode: true
+            }).then(res =>  {
+              if(res) {
+                axios.post('/api/v2/moderator/action', {
+                  action: action,
+                  item_id: status.id,
+                  item_type: 'status'
+                }).then(res => {
+                  swal('Success', 'Successfully added content warning', 'success');
+                  status.sensitive = false;
+                  self.ctxModMenuClose();
+                }).catch(err => {
+                  swal(
+                    'Error',
+                    'Something went wrong, please try again later.',
+                    'error'
+                  );
+                  self.ctxModMenuClose();
+                });
+              }
+            });
+          break;
+
+          case 'unlist':
+            msg = 'Are you sure you want to unlist this post?';
+            swal({
+              title: 'Confirm',
+              text: msg,
+              icon: 'warning',
+              buttons: true,
+              dangerMode: true
+            }).then(res =>  {
+              if(res) {
+                axios.post('/api/v2/moderator/action', {
+                  action: action,
+                  item_id: status.id,
+                  item_type: 'status'
+                }).then(res => {
+                  // this.feed = this.feed.filter(f => {
+                  //   return f.id != status.id;
+                  // });
+                  swal('Success', 'Successfully unlisted post', 'success');
+                  self.ctxModMenuClose();
+                }).catch(err => {
+                  self.ctxModMenuClose();
+                  swal(
+                    'Error',
+                    'Something went wrong, please try again later.',
+                    'error'
+                  );
+                });
+              }
+            });
+          break;
+        }
+      },
+
+      ctxMenu() {
+        this.$refs.ctxModal.show();
+        return;
+      },
+
+      closeCtxMenu(truncate) {
+        this.$refs.ctxModal.hide();
+      },
+
+      ctxModMenu() {
+        this.$refs.ctxModal.hide();
+        this.$refs.ctxModModal.show();
+      },
+
+      ctxModMenuClose() {
+        this.$refs.ctxModal.hide();
+        this.$refs.ctxModModal.hide();
+      },
+
+      ctxMenuCopyLink() {
+        let status = this.status;
+        navigator.clipboard.writeText(status.url);
+        this.closeCtxMenu();
+        return;
+      },
+
+      ctxMenuFollow() {
+        let id = this.status.account.id;
+        axios.post('/i/follow', {
+          item: id
+        }).then(res => {
+          let username = this.status.account.acct;
+          this.relationship.following = true;
+          this.$refs.ctxModal.hide();
+          setTimeout(function() {
+            swal('Follow successful!', 'You are now following ' + username, 'success');
+          }, 500);
+        });
+      },
+
+      ctxMenuUnfollow() {
+        let id = this.status.account.id;
+        axios.post('/i/follow', {
+          item: id
+        }).then(res => {
+          let username = this.status.account.acct;
+          this.relationship.following = false;
+          this.$refs.ctxModal.hide();
+          setTimeout(function() {
+            swal('Unfollow successful!', 'You are no longer following ' + username, 'success');
+          }, 500);
+        });
+      },
+    
     },
     },
 }
 }
 </script>
 </script>

+ 2 - 29
resources/assets/js/components/PostMenu.vue

@@ -57,35 +57,8 @@
 								<!-- a class="list-group-item font-weight-bold text-decoration-none" :href="status.url">Share</a>
 								<!-- a class="list-group-item font-weight-bold text-decoration-none" :href="status.url">Share</a>
 								<a class="list-group-item font-weight-bold text-decoration-none" :href="status.url">Embed</a> -->
 								<a class="list-group-item font-weight-bold text-decoration-none" :href="status.url">Embed</a> -->
 								<a class="list-group-item text-dark text-decoration-none" href="#" @click.prevent="hidePost(status)">Hide</a>
 								<a class="list-group-item text-dark text-decoration-none" href="#" @click.prevent="hidePost(status)">Hide</a>
-								<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-dark text-decoration-none" :href="reportUrl(status)">Report</a>
-								<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-dark text-decoration-none" @click.prevent="muteProfile(status)" href="#">Mute Profile</a>
-								<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-dark text-decoration-none" @click.prevent="blockProfile(status)" href="#">Block Profile</a>
-								<span v-if="activeSession == true && statusOwner(status) == true || profile.is_admin == true">
-									<a class="list-group-item text-danger text-decoration-none" @click.prevent="deletePost">Delete</a>
-								</span>
-								<span v-if="activeSession == true && profile.is_admin == true">
-									<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'autocw')" href="#">
-										<p class="mb-0">Enforce CW</p>
-										<p class="mb-0  small text-muted">Adds a CW to every post <br> made by this account.</p>
-									</a>
-									<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'noautolink')" href="#">
-										<p class="mb-0">No Autolinking</p>
-										<p class="mb-0 small text-muted">Do not transform mentions, <br> hashtags or urls into HTML.</p>
-									</a>
-									<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'unlisted')" href="#">
-										<p class="mb-0">Unlisted Posts</p>
-										<p class="mb-0 small text-muted">Removes account from <br> public/network timelines.</p>
-									</a>
-									<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'disable')" href="#">
-										<p class="mb-0">Disable Account</p>
-										<p class="mb-0 small text-muted">Temporarily disable account <br> until next time user log in.</p>
-									</a>
-									<a class="list-group-item text-dark text-decoration-none" v-on:click="moderatePost(status, 'suspend')" href="#">
-										<p class="mb-0">Suspend Account</p>
-										<p class="mb-0 small text-muted">This prevents any new interactions, <br> without deleting existing data.</p>
-									</a>
-
-								</span>
+								<a v-if="activeSession == true && !statusOwner(status)" class="list-group-item text-danger font-weight-bold text-decoration-none" :href="reportUrl(status)">Report</a>
+								<div v-if="activeSession == true && statusOwner(status) == true || profile.is_admin == true" class="list-group-item text-danger font-weight-bold cursor-pointer" @click.prevent="deletePost">Delete</div>
 							</div>
 							</div>
 						</div>
 						</div>
 					</div>
 					</div>

+ 12 - 3
resources/assets/js/components/Timeline.vue

@@ -438,11 +438,11 @@
 	size="sm"
 	size="sm"
 	body-class="list-group-flush p-0 rounded">
 	body-class="list-group-flush p-0 rounded">
 	<div class="list-group text-center">
 	<div class="list-group text-center">
-		<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuReportPost()">Report inappropriate</div>
+		<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuReportPost()">Report</div>
 		<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id && ctxMenuRelationship && ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuUnfollow()">Unfollow</div>
 		<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id && ctxMenuRelationship && ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-danger" @click="ctxMenuUnfollow()">Unfollow</div>
 		<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id && ctxMenuRelationship && !ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-primary" @click="ctxMenuFollow()">Follow</div>
 		<div v-if="ctxMenuStatus && ctxMenuStatus.account.id != profile.id && ctxMenuRelationship && !ctxMenuRelationship.following" class="list-group-item rounded cursor-pointer font-weight-bold text-primary" @click="ctxMenuFollow()">Follow</div>
 		<div class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToPost()">Go to post</div>
 		<div class="list-group-item rounded cursor-pointer" @click="ctxMenuGoToPost()">Go to post</div>
-		<div v-if="ctxMenuStatus && ctxMenuStatus.local == true" class="list-group-item rounded cursor-pointer" @click="ctxMenuEmbed()">Embed</div>
+		<div v-if="ctxMenuStatus && ctxMenuStatus.local == true && !ctxMenuStatus.in_reply_to_id" class="list-group-item rounded cursor-pointer" @click="ctxMenuEmbed()">Embed</div>
 		<!-- <div class="list-group-item rounded cursor-pointer" @click="ctxMenuShare()">Share</div> -->
 		<!-- <div class="list-group-item rounded cursor-pointer" @click="ctxMenuShare()">Share</div> -->
 		<div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">Copy Link</div>
 		<div class="list-group-item rounded cursor-pointer" @click="ctxMenuCopyLink()">Copy Link</div>
 		<div v-if="profile && profile.is_admin == true" class="list-group-item rounded cursor-pointer" @click="ctxModMenuShow()">Moderation Tools</div>
 		<div v-if="profile && profile.is_admin == true" class="list-group-item rounded cursor-pointer" @click="ctxModMenuShow()">Moderation Tools</div>
@@ -558,6 +558,10 @@
 				</span>
 				</span>
 			</div>
 			</div>
 			<div class="d-flex align-items-center">
 			<div class="d-flex align-items-center">
+				<div class="custom-control custom-switch mr-3">
+					<input type="checkbox" class="custom-control-input" id="replyModalCWSwitch" v-model="replyNsfw">
+					<label :class="[replyNsfw ? 'custom-control-label font-weight-bold text-dark':'custom-control-label text-lighter']" for="replyModalCWSwitch">Mark as NSFW</label>
+				</div>
 				<!-- <select class="custom-select custom-select-sm my-0 mr-2">
 				<!-- <select class="custom-select custom-select-sm my-0 mr-2">
 					<option value="public" selected="">Public</option>
 					<option value="public" selected="">Public</option>
 					<option value="unlisted">Unlisted</option>
 					<option value="unlisted">Unlisted</option>
@@ -675,6 +679,7 @@
 				showReadMore: true,
 				showReadMore: true,
 				replyStatus: {},
 				replyStatus: {},
 				replyText: '',
 				replyText: '',
+				replyNsfw: false,
 				emoji: window.App.util.emoji,
 				emoji: window.App.util.emoji,
 				showHashtagPosts: false,
 				showHashtagPosts: false,
 				hashtagPosts: [],
 				hashtagPosts: [],
@@ -697,6 +702,7 @@
 				mpPoller: null
 				mpPoller: null
 			}
 			}
 		},
 		},
+
 		watch: {
 		watch: {
 			ctxEmbedShowCaption: function (n,o) {
 			ctxEmbedShowCaption: function (n,o) {
 				if(n == true) {
 				if(n == true) {
@@ -721,6 +727,7 @@
 				this.ctxEmbedPayload = window.App.util.embed.post(this.ctxMenuStatus.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
 				this.ctxEmbedPayload = window.App.util.embed.post(this.ctxMenuStatus.url, this.ctxEmbedShowCaption, this.ctxEmbedShowLikes, mode);
 			}
 			}
 		},
 		},
+
 		beforeMount() {
 		beforeMount() {
 			this.fetchProfile();
 			this.fetchProfile();
 			this.fetchTimelineApi();
 			this.fetchTimelineApi();
@@ -1072,7 +1079,8 @@
 				}
 				}
 				axios.post('/i/comment', {
 				axios.post('/i/comment', {
 					item: id,
 					item: id,
-					comment: comment
+					comment: comment,
+					sensitive: this.replyNsfw
 				}).then(res => {
 				}).then(res => {
 					this.replyText = '';
 					this.replyText = '';
 					this.replies.unshift(res.data.entity);
 					this.replies.unshift(res.data.entity);
@@ -1663,6 +1671,7 @@
 				}, 500);
 				}, 500);
 			},
 			},
 		},
 		},
+
 		beforeDestroy () {
 		beforeDestroy () {
 			clearInterval(this.mpInterval);
 			clearInterval(this.mpInterval);
 		},
 		},