Browse Source

Merge pull request #516 from pixelfed/frontend-ui-refactor

Frontend ui refactor
daniel 6 years ago
parent
commit
7263363028

+ 10 - 0
app/Collection.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Collection extends Model
+{
+    //
+}

+ 10 - 0
app/CollectionItem.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace App;
+
+use Illuminate\Database\Eloquent\Model;
+
+class CollectionItem extends Model
+{
+    //
+}

+ 29 - 0
app/DirectMessage.php

@@ -0,0 +1,29 @@
+<?php
+
+namespace App;
+
+use Auth;
+use Illuminate\Database\Eloquent\Model;
+
+class DirectMessage extends Model
+{
+    public function status()
+    {
+    	return $this->hasOne(Status::class, 'id', 'status_id');
+    }
+
+    public function url()
+    {
+    	return url('/i/message/' . $this->to_id . '/' . $this->id);
+    }
+
+    public function author()
+    {
+    	return $this->hasOne(Profile::class, 'id', 'from_id');
+    }
+
+    public function me()
+    {
+    	return Auth::user()->profile->id === $this->from_id;
+    }
+}

+ 43 - 0
app/Http/Controllers/Api/BaseApiController.php

@@ -6,8 +6,11 @@ use App\Avatar;
 use App\Http\Controllers\AvatarController;
 use App\Http\Controllers\Controller;
 use App\Jobs\AvatarPipeline\AvatarOptimize;
+use App\Jobs\ImageOptimizePipeline\ImageOptimize;
+use App\Media;
 use App\Profile;
 use App\Transformer\Api\AccountTransformer;
+use App\Transformer\Api\MediaTransformer;
 use App\Transformer\Api\StatusTransformer;
 use Auth;
 use Cache;
@@ -115,4 +118,44 @@ class BaseApiController extends Controller
             'msg'  => 'Avatar successfully updated',
         ]);
     }
+
+    public function uploadMedia(Request $request)
+    {
+        $this->validate($request, [
+              'file.*'      => function() {
+                return [
+                    'required',
+                    'mimes:' . config('pixelfed.media_types'),
+                    'max:' . config('pixelfed.max_photo_size'),
+                ];
+              },
+        ]);
+        $user = Auth::user();
+        $profile = $user->profile;
+        $monthHash = hash('sha1', date('Y').date('m'));
+        $userHash = hash('sha1', $user->id.(string) $user->created_at);
+        $photo = $request->file('file');
+
+        $storagePath = "public/m/{$monthHash}/{$userHash}";
+        $path = $photo->store($storagePath);
+        $hash = \hash_file('sha256', $photo);
+
+        $media = new Media();
+        $media->status_id = null;
+        $media->profile_id = $profile->id;
+        $media->user_id = $user->id;
+        $media->media_path = $path;
+        $media->original_sha256 = $hash;
+        $media->size = $photo->getClientSize();
+        $media->mime = $photo->getClientMimeType();
+        $media->filter_class = null;
+        $media->filter_name = null;
+        $media->save();
+
+        ImageOptimize::dispatch($media);
+        $resource = new Fractal\Resource\Item($media, new MediaTransformer());
+        $res = $this->fractal->createData($resource)->toArray();
+
+        return response()->json($res);
+    }
 }

+ 3 - 0
app/Http/Controllers/StatusController.php

@@ -110,7 +110,10 @@ class StatusController extends Controller
         $status->profile_id = $profile->id;
         $status->caption = strip_tags($request->caption);
         $status->is_nsfw = $cw;
+
+        // TODO: remove deprecated visibility in favor of scope
         $status->visibility = $visibility;
+        $status->scope = $visibility;
 
         $status->save();
 

+ 4 - 3
app/Transformer/ActivityPub/ProfileTransformer.php

@@ -39,9 +39,10 @@ class ProfileTransformer extends Fractal\TransformerAbstract
             'owner'        => $profile->permalink(),
             'publicKeyPem' => $profile->public_key,
           ],
-          'endpoints' => [
-            'sharedInbox' => config('routes.api.sharedInbox'),
-          ],
+          // remove shared inbox support until proper support
+          // 'endpoints' => [
+          //   'sharedInbox' => config('routes.api.sharedInbox'),
+          // ],
           'icon' => [
             'type'      => 'Image',
             'mediaType' => 'image/jpeg',

+ 34 - 0
database/migrations/2018_10_17_233623_update_follower_table_add_remote_flags.php

@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+
+class UpdateFollowerTableAddRemoteFlags extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('followers', function (Blueprint $table) {
+            $table->boolean('local_profile')->default(true)->index()->after('following_id');
+            $table->boolean('local_following')->default(true)->index()->after('local_profile');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('followers', function (Blueprint $table) {
+            $table->dropColumn('local_profile');
+            $table->dropColumn('local_following');
+        });
+    }
+}

+ 147 - 0
resources/assets/js/components/PostComments.vue

@@ -0,0 +1,147 @@
+<style type="text/css">
+.b-dropdown > button {
+  padding:0 !important;
+}
+</style>
+<style scoped>
+ span {
+  font-size: 14px;
+ }
+ .comment-text {
+  word-break: break-all;
+ }
+ .b-dropdown {
+    padding:0 !important;
+ }
+.b-dropdown < button {
+ }
+ .lds-ring {
+  display: inline-block;
+  position: relative;
+  width: 64px;
+  height: 64px;
+}
+.lds-ring div {
+  box-sizing: border-box;
+  display: block;
+  position: absolute;
+  width: 51px;
+  height: 51px;
+  margin: 6px;
+  border: 6px solid #6c757d;
+  border-radius: 50%;
+  animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
+  border-color: #6c757d transparent transparent transparent;
+}
+.lds-ring div:nth-child(1) {
+  animation-delay: -0.45s;
+}
+.lds-ring div:nth-child(2) {
+  animation-delay: -0.3s;
+}
+.lds-ring div:nth-child(3) {
+  animation-delay: -0.15s;
+}
+@keyframes lds-ring {
+  0% {
+    transform: rotate(0deg);
+  }
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+</style>
+
+<template>
+<div>
+  <div class="lwrapper text-center">
+    <div class="lds-ring"><div></div><div></div><div></div><div></div></div> 
+  </div>
+  <div class="cwrapper d-none">
+    <p class="mb-1 text-center load-more-link"><a href="#" class="text-muted" v-on:click="loadMore">Load more comments</a></p>
+    <div class="comments" data-min-id="0" data-max-id="0">
+      <p class="mb-2 d-flex justify-content-between align-items-center" v-for="(comment, index) in results" :data-id="comment.id" v-bind:key="comment.id">
+        <span class="pr-3">
+          <span class="font-weight-bold pr-1"><bdi><a class="text-dark" :href="comment.account.url">{{comment.account.username}}</a></bdi></span>
+          <span class="comment-text" v-html="comment.content"></span>
+        </span>
+        <b-dropdown :id="comment.uri" variant="link" no-caret class="float-right">
+          <template slot="button-content">
+              <i class="fas fa-ellipsis-v text-muted"></i><span class="sr-only">Options</span>
+          </template>
+          <b-dropdown-item class="font-weight-bold" v-on:click="reply(comment)">Reply</b-dropdown-item>
+          <b-dropdown-item class="font-weight-bold" :href="comment.url">Permalink</b-dropdown-item>
+          <b-dropdown-item class="font-weight-bold" v-on:click="embed(comment)">Embed</b-dropdown-item>
+          <b-dropdown-item class="font-weight-bold" :href="comment.account.url">Profile</b-dropdown-item>
+          <b-dropdown-divider></b-dropdown-divider>
+          <b-dropdown-item class="font-weight-bold" :href="'/i/report?type=post&id='+comment.id">Report</b-dropdown-item>
+        </b-dropdown>
+      </p>
+    </div>
+  </div>
+</div>
+</template>
+
+<script>
+export default {
+    props: ['post-id', 'post-username'],
+    data() {
+        return {
+            results: {},
+            pagination: {},
+            min_id: 0,
+            max_id: 0,
+            reply_to_profile_id: 0,
+          }
+    },
+    mounted() {
+      this.fetchData();
+    },
+    methods: {
+      embed(e) {
+          pixelfed.embed.build(e);
+      },
+      reply(e) {
+          this.reply_to_profile_id = e.account.id;
+          $('.comment-form input[name=comment]').val('@'+e.account.username+' ');
+          $('.comment-form input[name=comment]').focus();
+      },
+      fetchData() {
+          let url = '/api/v2/comments/'+this.postUsername+'/status/'+this.postId;
+          axios.get(url)
+            .then(response => {
+                let self = this;
+                this.results = response.data.data;
+                this.pagination = response.data.meta.pagination;
+                $('.lwrapper').addClass('d-none');
+                $('.cwrapper').removeClass('d-none');
+            }).catch(error => {
+                $('.lds-ring').attr('style','width:100%').addClass('pt-4 font-weight-bold text-muted').text('An error occured, cannot fetch comments. Please try again later.');
+                console.log(error);
+            });
+      },
+      loadMore(e) {
+          e.preventDefault();
+          if(this.pagination.total_pages == 1 || this.pagination.current_page == this.pagination.total_pages) {
+            $('.load-more-link').addClass('d-none');
+            return;
+          }
+          $('.cwrapper').addClass('d-none');
+          $('.lwrapper').removeClass('d-none');
+          let next = this.pagination.links.next;
+          axios.get(next)
+            .then(response => {
+                let self = this;
+                let res =  response.data.data;
+                $('.lwrapper').addClass('d-none');
+                for(let i=0; i < res.length; i++) {
+                  this.results.unshift(res[i]);
+                }
+                $('.cwrapper').removeClass('d-none');
+                this.pagination = response.data.meta.pagination;
+            });
+      }
+    }
+}
+</script>

+ 1 - 2
resources/assets/js/components/bookmarkform.js

@@ -1,6 +1,5 @@
 $(document).ready(function() {
-
-  $('.bookmark-form').submit(function(e) {
+  $(document).on('submit', '.bookmark-form', function(e) {
     e.preventDefault();
     var el = $(this);
     var id = el.data('id');

+ 2 - 2
resources/assets/js/components/commentform.js

@@ -35,9 +35,9 @@ $(document).ready(function() {
         var comments = el.parents().eq(1).find('.comments');
       }
 
-      var comment = '<p class="mb-0"><span class="font-weight-bold pr-1"><bdi><a class="text-dark" href="' + profile + '">' + username + '</a></bdi></span><span class="comment-text">'+ reply + '</span><span class="float-right"><a href="' + permalink + '" class="text-dark small font-weight-bold">1s</a></span></p>';
+      var comment = '<p class="mb-2"><span class="font-weight-bold pr-1"><bdi><a class="text-dark" href="' + profile + '">' + username + '</a></bdi></span><span class="comment-text">'+ reply + '</span></p>';
 
-      comments.append(comment);
+      comments.prepend(comment);
       
       commentform.val('');
       commentform.blur();

+ 1 - 4
resources/views/layouts/partial/footer.blade.php

@@ -7,11 +7,8 @@
           <a href="{{route('site.terms')}}" class="text-primary pr-3">Terms</a>
           <a href="{{route('site.privacy')}}" class="text-primary pr-3">Privacy</a>
           <a href="{{route('site.platform')}}" class="text-primary pr-3">API</a>
-          <a href="#" class="text-primary pr-3">Directory</a>
-          <a href="#" class="text-primary pr-3">Profiles</a>
-          <a href="#" class="text-primary pr-3">Hashtags</a>
           <a href="{{route('site.language')}}" class="text-primary pr-3">Language</a>
-          <a href="http://pixelfed.org" class="text-muted float-right" rel="noopener">Powered by PixelFed</a>
+          <a href="https://pixelfed.org" class="text-muted float-right" rel="noopener">Powered by PixelFed</a>
         </p>
     </div>
   </footer>

+ 2 - 0
storage/purify/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore